SAGA2 (Final Fantasy Legend II) Technical Notes

version 0.3.0

*************
MEMORY LAYOUT
*************

rom:
bank00
  0000..0103 code (reset/interrupt vectors, low level utility)
  0104..014f rom header
  0150..18ff code (math, scripting, video, misc)
  1900..3fff code (map mode, commands)
bank01
  4000..42ff oam templates
  4300..43ff npc gfx -> animation type (not all used in practice)
  4400..4fff unused (?)
  5000..6fff code (menus)
  7000..7125 battle animation frame data (index)
  7126..7e27 battle animation frame data (data) >battle_anim_tile_info
  7e28..7fff unused
bank02
  4000..7fff map tile gfx
bank03
  4000..7fff npc gfx
bank04
  4000..46ff npc gfx (can't use for player)
  4700..47ff standard battle misc tiles (trash, (x), ?, (c), (resist), (weak), HP, hearts)
  4800..4fff standard font and window tiles
  5000..57ff title screen logo tiles
  5800..5fff end screen tiles
  6000..7fff battle animation tiles
  6000..65ff standard animation tiles
  6330..63bf arsenal cannon explosion tiles (1)
  6420..643f circles tiles
  6440..649f arsenal cannon explosion tiles (2)
  64b0..64df heart song tiles
  64e0..652f curse song tiles
  65f0..65ff curse song tiles
  6ea0..6edf music note tiles
  7100..710f PBA_RAIN tile
  73a0..73df question mark tiles
  7930..794f gather tiles
  7c50..7c8f meat tiles
  7c90..7d1f arsenal launch smasher fireball tiles
  7d20..7e9f arsenal launch smasher background tiles
  7ea0..7eff arsenal launch smasher animation start tiles
  7f40..7f6f arsenal cannon beam tiles
  7fe0..7fef enemy defeated animation tile
bank05
  4000..63ff monster gfx 2 (6x6) (0x10)
  6400..7eff monster gfx 8 (6x6) (0x0c)
  7f00..7fff monster gfx 7 (4x4) (0x01)
bank06
  4000..46ff monster gfx 0 (4x4) (0x07)
  4700..58ff monster gfx 1 (4x6) (0x0c)
  5900..5dff monster gfx 4 (8x6) (0x02)
  5f00..70ff monster gfx 3 (6x8) (0x06)
  7100..78ff monster gfx 5 (8x8) (0x02)
  7900..7fff monster gfx 6 (ex8) (0x01)
bank07
  4000..47ff doors (1, 2) (last door is unused)
  4800..66ff map headers
  6700..67ff battle animation metadata (last 0x15 slots appear unused)
  6800..68ff battle animation stream index (last 0x15 slots appear unused)
  6900..6eb8 battle animation data streams
  6eb9..6f7e (unused) (?)
  6f7f       battle animation data stream sentinel (?)
  6f80..6fff doors (3)
  7000..7fff tile sets
bank08
  4000..7fff tile maps (1)
bank09
  4000..7fff tile maps (2)
bank0a
  4000..7e3e scripts (2) // map scripts
  ?
bank0b
  4000..6b7f scripts (1) // map scripts
  6b80..73ff scripts (3) // battle messages
  7400..7fcf box scripts
  7fd0..7fff (unused)
bank0c
  4000..467f code (cscripting)
  4680..4695 cscript entry point index
  4696..6efe cscript data
  6eff       unused (?)
  6f00..6f7f enemy item probability tables
  6f80..777f >item_data item stats (unified indexing with magi)
  7780..77ff >item_data magi stats (also includes "status items")
  7800..78ff monster species data (high nybble: family, low nybble: species index)
  7900..7b3f monster transformation table
  7b40..7c3f >robot_item_data item stats
  7c40..7c4f >robot_item_data magi stats
  7c50..7d4f monster gold and drop info
  7d50..7e4f high nybble index into 6f00 probability table, low nybble "DS level"
  7e50..7e7f gold reward lookup table (0x18 entries at 2 bytes each)
  7e80..7f7f item usage
  7f80..7f8f magi usage
  7f90..7faf human/mutant growth table
  7fb0..7fcf ability learning treshold
  7fd0..7fef ability learning result
  7ff0..7ff2 constant 999999 (decimal)
  7ff3..7fff (unused ?)
bank0d
  4000..4fff code (battle mode)
  5000..63ff code (battle animation)
  6400..64ff monster -> monster gfx (high nybble is size-type, low is index)
  6500..6508 monster gfx size-type -> size (high/low nybble)
  6509..650f (unused)
  6510..652a monster gfx addr info (by size-type)
  6540..6548 monster gfx x coords (depending on # of groups; for battle animation)
  6549..654f (unused) (?)
  6550..6558 monster gfx size-type -> y offset
  6559..655f (unused) (?)
  6560..6566 encounter probability table
  6567..656f (unused) (?)
  6570..69a7 encounter set tables
  69a8..6b6f (unused) probably safe for more encounter set tables up to 0xc0 (?)
  6b70..6c6f npc class -> npc gfx table
  6c70..6eef >encounter_data (0x80 * 5)
  6ef0..6f4f encounter numbers
  6f50..6f7f (unused) (?)
  6f80..797f monster data >monster_data
  7980..7eb4 monster equipment lists
  7eb5..7fff unused
bank0e
  4000..49f4 code (audio)
  49f5..4a66 music stream addresses (3 channels for each track, 0x13 tracks)
  4a67       unused or sentinel (?)
  4a68..79fd music stream data
  79fe..7a30 pitch effect streams
  7a31..7a75 volume effect streams (1 byte overlap with first wave sample)
  7a75..7ac4 wave samples (0x10 bytes each)
  7ac5..7b36 sound tone stream addresses (0x39 sounds)
  7b37..7ba8 sound noise addresses
  7ba9..7fe8 sound stream data
  7fe9..7fff unused
bank0f
  4000..40ff rng sequence
  4100..41ff sine lookup table
  4200..422f oam template for window sprites (tile and flags only)
  4230..4237 oam template for window sprites (x and y only)
  4238..423f status priority table
  4240..4248 status display table (x9 for no-status) (as the first byte of >window_sprite_entry & f8)
  4249..424f (unused) (?)
  4250..426f memo bank names -- index
  4270..446f memo scripts -- index
  4470..44e1 memo bank names -- data
  44e2..6037 memo scripts -- data
  6038..607f unused (?) old location of some battle gfx code (see below)
  6080..655f code (battle gfx)
  6560..65ff digraph table
  6600..660f prism magi counts
  6610..663f guest names
  6640..6e3f item names (unified indexing with magi names)
  6e40..6ebf magi names
  6ec0..76bf monster names
  76c0..77bf warp names
  77c0..783f consecutive numbers 00..7f (used as tile indices for title logo)
  7840..785f status names (4 bytes each for 8 statuses)
  7860..79df item prices (0x80 priced items at 3 bytes each)
  79e0..7a9f shop data (24 shops)
  ?
  7ad2..7ad4 unlimited use icon sys script
  7ad5..7ae1 trash icon sys script
  7ae2..7ae4 text-x sys script
  7ae5..7ae7 text-resist sys script
  7ae8..7aea text-weak sys script
  7aeb..7aed text-hyphen sys script (unused?)
  7aee..7afe jukebox lookup table
  7aff       unused (?)
  7b00..7b13 meat animation x/y offsets (0x0a frames)
  7b14..7b7f unused (?)
  7b80..7bab oam type index (for procedural battle animation)
  7bac..7d2c oam type data
  7d2d..7fff unused (?)
sram:
  a000..a17f save slot 1
  a180..a2ff save slot 2
  a300..a47f save slot 3
  a480..a5ff unused (?)
  a600..a77f save backup
  a780       save count
  a781..a782 sentinel value (0xe41b) to determine whether sram is setup (or corrupt maybe).
  a783..bfff unused (?)
wram:
  c000..c09f oam staging (1)
  c0a0..???? random seeds
  ????..c0df other random data (unused?)
  c0e0..c0e3 ram program used for divide_16_16 (restore sp)
  c0e4..c0e8 ram program used for long call
  c0e9..c0eb ram program used for bit functions
  ?
  c100..c19f oam staging (2) (alternate animation frame for sprites)
  c1a0       unknown cscript data (related to instruction 4, maybe unused) ?TODO?
  c1a1..c1a2 cscript assignment target address
  c1a3       cscript assignment format
  c1a4       cscript conditional expression operation (or flag)
  c1a5..c1a7 cscript accumulator
  c1a8..c1aa cscript temp value
  c1ab       cscript sign bit (temp)
  c1ac..c1ae cscript comparison test value
  c1af..c1b0 cscript stack pointer
  c1b1..c1ff cscript stack (grows upwards) (hard to verify that the whole region is actually reserved for this)
  c200..c37f saved RAM region (details below)
  c200..c29f player party
  c2a0       party order
  c2a1       defeated count
  c2a2..c2a4 party gp
  c2a5..c2a6 address of the current map header (saved)
  c2a7       saved player_x and direction
  c2a8       saved player_y
  c2a9..c2b8 vehicle_data[4]
  c2b9..c2d8 inventory
  c2d9       magi total count
  c2da..c2f5 magi array (0xe at 2 bytes each; first high nybble 1+equipped char, low nybble count, second byte usage (for heart))
  c2f6..c305 script variables (4-bit fields, high nybble first)
  c306..c315 chest flags
  c316..c319 exit door (generated each time a door is used in case the next door is the special "exit" door)
  c31a       map mode music (saved)
  c31b       text speed
  c31c       save count
  c31d..c33c memo flags
  c33d       saved player z
  c33e       saved c43a (why?)
  c33f       saved player transparency flags
  c340..c353 menu memory (2 bytes each, 0..4 is battle abilities) (at least to 9 is used)
  c354       teleport enabled flag
  ?
  c37c..c37d sentinel (when saved) to indicate that this save slot is used and valid (0xe41b)
  c37e..c37f save checksum
  c380..c3ff menu cursor stops array
  c400..c42b tile buffer (temp storage for drawing tiles into VRAM)
  c42c       camera x coordinate -- top left 16x16 tile (half on screen) (may be negative)
  c42d       camera y coordinate
  c42e       player x coordinate -- pixels relative to camera (almost always 0x50)
  c42f       player y coordinate --                           (almost always 0x40)
  c430       player sprite index
  c431       player animation type (oam template index)
  c432..c433 player oam template pointer (bank 1)
  c434       player z (0 neutral, 1 and 2 are flags)
  c435       player move dir and amount and camera flag (high nybble: amount, low nybble: 1 + dir and 08 camera move flag)
  c436       player face dir (0 down, 1 up, 2 left, 3 right)
  c437       conveyer speed (or 0 if player is moving, even on conveyer)
  c438       visited tile flag (has trigger etc already been handled)
  c439       tile animation state
  c43a       argument for STAGE_OAM_TEMPLATE (rom 00 @2a5d)
  c43b       player transparency flags
  c43c       current inside / outside flag (0x00: outside or 0x20: inside)
  c43d       player movement direction (0 is stationary -- hits this between tiles)
  c43e       player current speed (affected by conveyer tiles)
  c43f       player natural speed
  c440       npc processing counter (scripts use this to wait for npcs to finish moving)
  c441       frame counter used by shaking fx
  c442       current command (arg)  (0xff if no command)
  c443       current command (type) (0xff if no command)
  c444       current command npc (for npc commands) (or below)
  c444..c445 second command (for command pairs)
  c446       vehicle npc index backup (for map button vehicle enter)
  ?TODO?
  c449..c44a vram address of upper left visible 16x16 tile
  c44b..c44c address of current map header
  c44d       weird door transition flag (not used?)
  c44e..c44f address of current tile map
  c450       current tile set
  c451       number of trigger tiles in the tileset (tile 3 is the first)
  c452       current map has-npcs flag
  c453       current map has-encounters flag
  c454       current map encounter set
  c455       current map encounter rate
  c456       current map tile animation settings
  c457..c458 address of current map trigger data (part of header)
  c459..c45a address of current map npc graphics data (part of header)
  c45b       hide player flag
  c45c..c45f odin door saved door (or first two bytes 0 for none saved)
  c460       temp for MAP_BUTTON_HANDLER (rom 00 @3039) (fx backup)
  c461       temp for rom 00 @1b66 (fx backup)
  c462       temp for LAND_ON_TILE (rom 00 27c9), rom 00 @2ddc (fx backup)
  c463       temp for MAP_TRANSITION (rom 00 @1aec) (fx backup)
  c464       frame counter used by wave fx
  c465       temp for HANDLE_COMMAND (rom 00 @391d) (fx backup)
  c466       continue flag (0: new game, 1: continue)
  c467       temp for REFRESH_NPCS (rom 00 @1909)
  c468       top index for npc command saved rom bank stack (c4xx)
  c469..???? npc command saved rom bank stack
  ?
  c473..c476 lcd stat interrupt backup during certain fsfx (screen wipe)
  c473       temp (current x during tile map drawing)
  c474       temp (current y during tile map drawing)
  c475       temp (current line x during tile map drawing)
  c476       temp (current line y during tile map drawing)
  c477..c47d temp (various used during tile map drawing, other)
  c47e       temp (npc wander direction)
  c47f       temp (npc processing: oam offset)
  ?
  c4ff       frame counter (controls sprite animation)
  c500..c51f original tiles corresponding to triggers in the tile map
  c520..c53f current tile set tile flags
  c540..c5cf offsets for wavy fade in
  ?
  c600..c6ff npcs >npc
  c700..c702 palette mirror (?)
  c703       vblank interrupt program
  c704..c705 vblank interrupt program address
  c706       lcdstat interrupt program
  c707..c708 lcdstat interrupt program address
  c709       script / general player index
  c70a..c70c temp (3 byte value)
  c70d..???? script uint8 argument array
  ?
  c71d..c73c script item and usage argument array
  c73d..c744 script monster argument array[8]
  c745..c762 script uint24 argument array[10] (8 is not used?)
  c763       encounter result flag (0: victory; 1: defeat)
  c764       window enabled flag
  c765       bit 0 is text acceleration disable flag, bit 1 is text pause disable flag
  ?
  c770..c773 joypad raw state memory
  c774       joypad counter (frames until button repeat)
  c775       joypad previous state (for button repeat)
  c776..c777 sentinel value used to detect whether the game has soft reset (0xe41b)
  c778       soft reset flag (0 on first boot, 1 after soft reset)
  c779..c77a script stack pointer
  c77b       script text chomp flag
  c77c       current script stack level
  c77d       saved next script stack level
  c77e       window width (8x8 tiles)
  c77f       window text chars remaining in current line (?)
  c780       window text width
  c781..c782 window text pointer
  c783..c784 window line pointer (?)
  c785..???? script / window text buffer (sometimes used for other temp storage) (at least to c794)
  ?
  c796       window sprite (16x16) count
  c797       window scroll x offset (in 8x8 tiles) (always 0?)
  c798       window scroll y offset (in 8x8 tiles)
  c799       window address flag (?)
  c79a       box script scroll flag (will cause c7cd flag 02 to be set)
  c79b       box script x
  c79c       box script y
  c79d       box script width
  c79e       box script height
  c79f       box script last screen x
  c7a0       box script last screen y
  ?
  c7a4       window y offset (in 8x8 tiles)
  c7a5       window y offset (in pixels -- used for WY)
  c7a6..c7c5 >window_sprite_entry[8] window sprite data
  c7c6       window sprite frame counter
  c7c7       window sprite animation frame flag (0 or 1 on alternate animation frames)
  c7c8..c7c9 start of unscrolled window data (when scrolling)
  c7ca       menu cursor count
  c7cb       last current menu cursor y coord (8x8 window tiles)
  c7cc       last current menu cursor x coord (8x8 window tiles)
  c7cd       menu cursor mode (flags 01: two cursors; 02: scroll; 04: text cursor)
  c7ce       in two cursor mode, flag indicating that the currently active cursor is the second one
  c7cf       number of non-scrolling cursor stops (in scrolling mode, otherwise 0)
  c7d0       box script first screen x
  c7d1       box script first screen y
  c7d2       script window options (01: draw to VRAM flag, 02: frame flag)
  c7d3..c7d5 backup of LCD Stat interrupt program (when it is changed)
  c7d6       menu text cursor count / name input index
  c7d7..c7d8 saved stack pointer of START_MENU -- used to exit the start menu from deep in the callstack (e.g. warp)
  c7d9       script argument memo bank
  c7da       script memo bank index (used by #text-memo-bank)
  c7db..c7dc backup item >slot during equip operations
  c7dd       script argument save slot (maybe not used)
  c7de       use flipping OAM flag (if on, use c000 and c100, else use cc00 directly)
  c7df       OAM DMA upper address byte (0xc0 or 0xc1)
  c7e0..c7ed script argument magi
  c7ee..c701 name entry name backup
  ?
  c7f2       script argument memo index
  c7f3       current encounter id
  ?
  c800..caff window memory (1); also misc buffer (frequently for battle animation)
  cb00       music time divider
  cb01       music tempo
  cb02..cb49 >music_control
  cb4a       sound noise channel timer
  cb4b..cb61 unused? (reserved for audio)
  cb62..cbc3 backup of background music's cb00..cb61 while foreground music plays
  cbc4..cbc5 sound tone channel stream pointer
  cbc6..cbc7 sound noise channel stream pointer
  ?
  cc00..cc9f oam staging (3)
  cca0..xxxx script stack (grows up)
  xxxx..ceff z80 CPU stack (starts at cf00, grows down)
  cf00..cfff cscript registers, also used for interaction or other purposes (below)
  ?
  cfe0..cfe5 encounter monster data (count, type)
  cfe6       battle door saved player z
  cfe7       battle door saved c43a (why?)
  cfe8       battle door saved player transparency
  cfe9       battle music (0..2 -> 0x11..0x13; 3 is no change)
  cfea       >encounter_info
  ?
  cff0..cff1 cscript use item item id
  cff2       cscript use item source
  cff3       cscript use item target
  cff4       cscript use item result
  cff5       battle animation target
  ?
  
wram, map mode:
  d000..dfff tile map memory
  
wram, menu mode:
  d000..d3ff window memory (2)
  d400..d4ff name entry cursor to character map
  d500..d66f name entry cursor stops
  d670..d673 name entry script stack frame backup
  d674..d6ff name entry script info backup
  ?

wram, battle mode:
  d000..d7ff >battle_data[8] for party and enemies
  d800       ambush status (0: none, 1: player initiative, 2: enemy initiative)
  d801       run result flag
  d802       current battle actor index
  d803..d843 battle actor order array (5+9*3 2-bytes entries plus one space for 0xff terminator)
  d844       >encounter_info
  d845       meat drop flag
  d846       meat transformation flag
  d847       meat transformation result
  d848       current battle turn number
  d849..d84b ability list length of each enemy - 1 (>monster_data[0] low nybble)
  d84c       meat transformation player index
  d84d..d85c last used item array
  ?
  d85e       defeated flag
  ?
  d900..d905 text-uint16 arguments
  d906..???? 16-bit index arguments for text functions (in battle mode)
  ?
  d90c       script index (when executing scripts in battle mode)
  d90d..d90f enemy death animation flag (ff after death animation plays)
  d910       current battle animation
  ?
  d920       number of (initially non-empty) enemy groups
  d921..d926 first byte enemy graphics bank, second byte index (x3)
  d927..d92c enemy graphics dimension (width, height x3) (in 8x8 tiles)
  d92d..d932 enemy graphics offset (of first tile from start of tile data), size (number of tiles)
  d933..d935 x offset for each enemy group (not adjusted based on graphics size, used for animations)
  d936..d938 x offset for each enemy group (adjusted based on graphics size)
  d939..d93b y offset for each enemy group (adjusted based on graphics size)
  d93c       battle animation global animation flag (0 means animation of one enemy)
  d93d..d93e battle animation data stream current address
  d93f       battle animation y
  d940       battle animation x
  d941       battle animation current frame length
  d942       battle animation current frame flags
  d943..4972 battle animation buffer >battle_anim_tile_info; misc buffer during death anim
  d973       battle animation current base tile index
  d974       current battle animation target
  d975..d976 top left address during PBA_LIFT or death animation
  d977..d978 next clear / bottom left address during PBA_LIFT or death animation
  ?
  d97a       fade out flag (1 if battle animation has faded out monster gfx)
  d97b       WY backup (while using window to cover monster gfx during PBA)
  d97c       death animation counter
  d97d       monster gfx morph frame counter
  d97e       monster gfx morph source bank
  d97f..d980 monster gfx morph source address
  d981..d982 monster gfx morph destination address (vram)
  d983       monster gfx morph progress remaining (tiles / 2)
  d984       monster gfx morph start flag (setting this starts morph)
  d985       monster gfx morph finish flag (set when morph is finished)
  d986..d988 enemy "lifted" flags (PBA_LIFT)
  d989       missile animation flag (display for each enemy; use d98a)
  d98a       missile animation flip flags
  d98b       special encounter type: 0 normal, 1 skip spawn anim (f:62a6), 2 arsenal
  d98c..d9ed arsenal tile data backup (for launch smasher hatch animation)
  ?
  de00..de09 monster data temp buffer
  ?
  de10       ability list length of last loaded enemy - 1 (>monster_data[0] low nybble)
  ?

hram:
  ff80..ff87 OAM DMA procedure
  ff88       current rom bank
  ff89       joypad "raw" state (still massaged somewhat, but pure query)
  ff8a       joypad button (key repeat handled -- only set once per "press")
  ff8b       battle flag
  ff8c       last confirmed menu cursor selection
  ff8d       last confirmed menu cursor secondary selection (first choice when in 2 cursor mode)
  ?
  ff90       temp1
  ff91       temp2
  ff92       temp3
  ff93..ff95 temp for rom00:176c (window sprite handler) -- note this can be called from an interrupt!
  ff96       window sprite mode (flags 01: allow window sprites, 02: cursor)
  ff97       menu cursor y (in 8x8 tiles)
  ff98       menu cursor x (in 8x8 tiles)
  ff99       menu cursor (1st choice when on 2nd selection) y (in 8x8 tiles)
  ff9a       menu cursor (1st choice when on 2nd selection) x (in 8x8 tiles)
  ff9b       current menu cursor selection
  ff9c       guard to make sure function 16f9 isn't called by an interrupt while it is already in progress
  ff9d       item slot index
  ff9e       item swap flags
  ff9f       item swap success
  ffa0       script mode (0: box script, 1: map scripts b:4000, 2: map scripts a:4000, 3: battle scripts, 4: intro scroll, 5: memo-box)
  ffa1       script saved bank
  ffa2..ffa3 script instruction pointer
  ?
  ffa5       Arsenal cloud animation enable flag
  ffa6       Arsenal cloud animation row counter (0..7 first tiles, 8..0x0f second tiles)
  ffa7..ffa8 Arsenal cloud animation tile data address 1
  ffa9..ffaa Arsenal cloud animation tile data address 2
  ?
  ffb0       (write to play) background music id (most things use this)
  ffb1       (write to play) foreground music id
  ffb2       (write to play) sound id
  ffb3       currently playing background music
  ffb4       rolling 0..2 counter to select which music channel to process effects for
  ffb5       music channel 0 current note length
  ffb6       music channel 1 current note length
  ffb7       music channel 2 current note length
  ffb8       unused? (reserved for audio)
  ffb9       foreground music playing flag
  ffba..ffbb music current wave sample address
  ffbc       sound tone channel loop counter
  ffbd       sound noise channel loop counter
  ffbe..ffbf unused? (reserved for audio)
  ffc0       next fade-in type
  ffc1       scroll y base (may be adjusted by e.g. shaking)
  ffc2       scroll x base
  ffc3       screen shake dy (never read?)
  ffc4       screen shake dx (never read?)
  ?

**********
DATA USAGE
**********

random seeds:
00 ~0:3253 npc wander -- which npc
01 @0:3249 npc wander -- chance
05 @c:443e c script (expression)
06 @c:44d2 c script (constant)
07 @d:4466 monster random hp variance (high byte)
08 @d:4471 monster random hp variance (low byte)
09 @0:289b encounter chance
0a @0:2afb encounter selection
0b @0:2b76 monster count
0c @0:3db5 screen shake
0d @0:3dc7 screen shake
30    ...  particle x
31    ...  particle y
32    ...  particle start delay

doors:
1fe ~0:19e0 new game door

script variables: (starting at c2f6, high nybble first)
var00 (var00)              guest (0 will make guest leave; otherwise still need #guest)
var11 (var0b)              disables post encounter script (Odin) if greater than 0
var14 (var0e)              prism index
var24 (var18)              warps unlocked
var29 (var1d)              underwater (0: normal; 2: underwater; 1: ?) -- cleared when player warps, no direct effect
var30 (var1e) 0xf0 & *c305 fx (0: none; 1 shake; 2 wave; 3: clear)
var31 (var1f) 0x0f & *c305 vehicle (0: none; 1..4 dragons of various speeds)

scripts:
0003       @0:0f05 post encounter script (if var0b is 0) (Odin)
0060..006f @1:5cc0 warps
0070       @1:68d3 opening text scroll (also runs in a special mode)
0103       @0:3147 empty chest message
0104       @0:3f2e inventory full message
0106       @0:3f34 drop item prompt
0107       @0:3f1e chest script

battle messages:
00 in cscript 00: player surprise
01 in cscript 00: enemy surprise
02 in cscript 01: run failed
03 in cscript 01: run succeeded

box scripts:
09 @01:5584 inventory list
10 @01:5f66 alter order player
11 @01:5f67 alter order guest
14 @01:6873 system menu
15 @01:613c character select
16 @01:6727 save slot 1
17 ~01:6727 save slot 2 (offest)
18 ~01:6727 save slot 3 (offset)
19 @01:54dd save confirm
1a @01:629f inn message
1b @01:6299 inn player summary
1c @01:629a inn guest summary
1d @01:62a2 inn party gold display
1e @01:62bd inn menu
1f @01:62fa inn not enough gp
20 @01:64e4 shop top menu (buy, sell, exit)
21 @01:64de shop gp
22 @01:64e1 shop may I help you
23 @01:6506 shop buy list
24 @01:6575 shop you got X
25 @01:659b shop not enough gp
26 @01:65af shop sell inv
27 @01:65a7 shop sell which item?
28 @01:661e shop sell prompt
29 @01:62f3 inn thanks come again
2a @01:     inn goodbye
2b @01:6584 shop inventory full
30          in-battle player summary
30 @01:6cb3   (meat select)
31          in-battle guest summary
32 @01:6aea in-battle monster summary (names & counts)
33 @01:6afd in-battle fight / run
34 @01:6b56 in-battle single player summary
35 @01:6ba2 in-battle item chooser
36 @01:6c10 in-battle monster select
37 @01:6c97 in-battle eat prompt
38 @01:6c19 in-battle target all prompt
39 @01:6b87 in-battle no action available
45 @00:0fb2 jukebox

cscripts: (battle scripts)
00 handle surprise attacks
02 monster ai
03 resolve turn order

sounds:
13 @01:...  invalid
22 @01:656b buy item
33 @01:62e0 inn
37 @01:...  confirm
38 @0d:4140 run away

***************
DATA STRUCTURES
***************
  
<player> size=0x20
char8[4] name       +00 c200
uint8    monster_id +04 c204 Determines sprite and class name
uint8    race       +05 c205 >race (e.g. 3 is robot)
uint8    status     +06 c206 >status
uint16   current_hp +07 c207
uint16   maximum_hp +09 c209
uint8    str        +0b c20b
uint8    agl        +0c c20c
uint8    mana       +0d c20d
uint8    def        +0e c20e
slot[8]  equipment  +0f c20f >slot
uint8    magi       +1f c21f (low byte of item id only)

<slot>
uint8    item id
uint8    usage; 0xfe means "-"

<status> flags
01 Slep
02 Para
04 Pois
08 Conf
10 Ston
20 Curs
40 Blnd
80 Stun

<npc>
 size = 0x10
+0    c600
  bit 7     empty slot flag
  bit 6     don't wander flag
  bit 0..5  x position
+1    c601  dx
+2    c602  y position
+3    c603  dy
+4    c604  number of queued tiles to move
+5    c605
  bit 6..7  movement direction
  bit 4..5  facing
  bit 0..3  oam template
+6    c606  transparency flags (0x10 and 0x20)
+7    c607  oam low byte index (0x10 for first, etc)
+8    c608  sprite
+9..a c609  command
+b    c60b  z
+c    c60c  has to do with if it can move, if you can trigger with button
+d    c60d  bump flag
+e..f c60e  ?

<direction> (sometimes direction + 1 is used)
-1 none
0 down
1 up
2 left
3 right

<race>
0 human
1 mutant
2 monster
3 robot

<resist> flags
01 Quake
02 Weapon
04 Stone
08 Para
10 Poison
20 Thunder
40 Ice
80 Fire

<item_data>
 size = 8
+00     flags
          01 battle usable
          02 menu usable
          04 can't unequip
          08 (unused)
          10 targets all
          20 targets enemies
          40 (unused)
          80 melee (AI targets based on party order)
+01     flags
          01 is counter attack
          02 is danger skin
          04 is burning
          08 is shield
          10 reflect magic
          20 elemental magi boost
          40 is warning
          80 is surprise
+02     flags
          01 helmet slot
          02 body armor slot
          04 gauntlet slot
          08 shoe slot
          10 resist
          20 weakness
          40 stat bonus
          80 no reduce (when robot equips/unequips)
+03     use script index (00..31)
+04     mulltiple meanings
          attack multplier (for many scripts)
          def bonus (if any armor slot flag is set)
          others (depending on script)
+05     multiple meanings (maybe simultaneously e.g. Parasuit)
          resist flags (if resist flag is set) >resist
          weakness flags (if weakness flag is set) >resist
          stat bonus parameters (if stat bonus flag is set)
            upper nybble
              80 str bonus
              40 agl bonus
              20 mana bonus
              10 def bonus (not used?)
            lower nybble
              amount
          others (depending on script)
+06     animation id (if applicable)
+07     sound id (if applicable)

<robot_item_data>
 size = 1
+00     upper nybble
          80 str bonus
          40 agl bonus
          20 (disabled)
          10 def bonus
        lower nybble
          amount
            hp: (x+1) * 9
            other stats: (x+1) * 2

<encounter_data>
+00          monster id 1
+01          monster id 2
+02          monster id 3
+03          <encounter_info>
   bit 6..7  music (0..2 -> 0x11..0x13; 3 -> no change)
   bit 5     no escape flag
   bit 0..4  encounter group sizes index
+04          alternate >encounter_info (when high bit of encounter id is set)

<monster_data>
+00     uint8          high nybble: race; low nybble: ability list length minus one
+01     uint8          always zero ?
+02..03 uint16         hp
+04     uint8          str
+05     uint8          agl
+06     uint8          mana
+07     uint8          def
+08..09 uint16         ability list pointer

<battle_data>
 size = 100
+00     uint8          stack size (maximum)
+01     uint8          stack size (current)
+02..09 char[8]        name
+0a     uint8          monster id
+0b     uint8          race
+0c..0d uint16         natural hp
+0e     uint8          str
+0f     uint8          agl
+10     uint8          mana
+11     uint8          def
+12..2c battle_item[9] inventory
+2d     uint8          resist flags
+2e     uint8          weak flags
  ?                    (probably unused)
+40..87 battle_stat[9] current_stat (9 is probably not a hard limit, but 27 total is)
  ?                    (probably unused)

<battle_item>
 size = 3
+00..01 uint16         item number (high byte is 0 for items, 1 for magi or special status programs)
+02     uint8          uses (0xfe is unlimited)

<battle_stat>
 size = 8
+00     uint8          >status
+01..02 uint16         hp
+03..04 uint16         current item choice (item index) (ff 00 is empty)
+05     uint8          current target choice
+06     uint8          current item choice (index in inventory)
?

<animation_type>
00 flip      -- front/back animation is done by flipping the frame
01 mirror    -- front/back animation is mirrored with independent animation frames, also animates on conveyers
02 half-flip -- front/back animation is done by flipping the bottom 8 rows of the sprite
03 fixed     -- no animation, each direction is an independent frame

<window_sprite_entry>
+00     uint8          packed
                         80 enabled
                         40 animation enabled
                         20 blink
                         10 forward/backward (0 is forward)
                         08 use grey palette
                         07 index (for OAM and VRAM tiles)
+01     uint8          packed
                         04 left/right wiggle (entailed by animation type)
                         03 animation type
+02     uint8          y (in pixels)
+03     uint8          x (in pixels)

<command>
(Note: NPC commands and trigger commands only get a 4-bit control)
+00     uint8          control
                         00     scripts (1)
                         01..03 scripts (2) (there are only 256 scripts here but the engine seems to allow more)
                         04     shop
                         05..06 doors (1, 2) 
                         07     audio
                         08     unused
                         09     item (chest)
                         0a     magi (chest)
                         0b     item (chest, force)
                         0c..0d battle doors (1, 2)
                         0e     doors (3)
                         0f     unused
                         f*     see #command
                         ...    unused
+01     uint8          parameter

<tile_info>
  bit 6..7  type (0: normal, 1: damage, 2: conveyer, 3: trigger)
  bit 5     transparency flag (upper)
  bit 4     transparency flag (lower)
  bit 3     block vehicle
  bit 2     bridge
  bit 1     block upper
  bit 0     block lower
conveyer:
  bit 2..3  conveyer speed
  bit 0..1  conveyer direction >facing
trigger:
  bit 5     disable
  bit 0..4  trigger index

<battle_anim_tile_info>
bit 7     RLE flag; next byte indicates length (1:7000 region only)
bit 6     Y flip (matching OAM flag)
bit 5     X flip (matching OAM flag)
bit 0..4  tile index (offset from base value stored at d973)

<music_control>
+00     ...  cb1a ...   sound timer (music channel muted while sound playing)
+01     cb03 cb1b cb33  music timer
+02..03 cb04 cb1c cb34  event stream pointer
+04     cb06 cb1e cb36  pitch effect timer
+05..06 cb07 cb1f cb37  starting pitch control address
+07..08 cb09 cb21 cb39  current pitch control address
+09     cb0b cb23 cb3b  note offset ("octave")
+0a     cb0c cb24 ...   last NRx1 assign (wave duty)
+0b     cb0d cb25 cb3d  last NRx3 assign (freq low)
+0c     cb0e cb26 cb3e  last NRx4 assign (freq high and control)
+0d     cb0f cb27 cb3f  loop counter
+0e     ...  ...  cb40  wave volume (NR32)
+0f     cb11 cb29 cb41  last note index (as encoded; relative to octave) (0x0f rest)
+10     ...  cb2a ...   pan
+11     cb13 cb2b cb43  enable flag (00 on, ff off)
+12     cb14 cb2c ...   volume effect timer
+13..14 cb15 cb2d ...   starting volume control address
+15..16 cb17 cb2f ...   current volume control address
+17     cb19 cb31 cb49  alt loop counter

*************
FUNCTION LIST
*************

00:0000..0006 function: ADD_16_8
  rom00:0000, rst 00     
    saved:     bcde, zf
    inputs:    hl <-- addend
                a <-- addend
    outputs:   hl <-- sum
               cf <-- carry
00:0007       unused (?)
00:0008..000a entry point for 0908
00:000b..000f common return block used by many functions
00:0010..0012 entry point for 00d9
00:0013..0014 address of standard LCD stat interrupt function
00:0015..0017 unused (?)
00:0018..001a function: OAM_DMA
  rom00:0018, rst 18
    saved:      bcdehl, cf
    inputs:     a <-- high byte of DMA source
    outputs:    a <-- 0
               zf <-- 0
00:001b..001f unused (?)
00:0020..0022 entry point for 0800
00:0023..0027 unused (?)
00:0028..002c function: BANK_SWITCH_SAFE
  rom0:0028, rst 28
    saved:     fbcdehl
    inputs:     a <-- new rom bank
    outputs:    a <-- old rom bank
    notes:
      "Safe" is in reference to the fact that interrupts are disabled during processing.
      Contrast with BANK_SWITCH which does not disable interrupts.
      The first time this is called after a reset, the return value will not be accurate.
00:002d..002f unused (?)
00:0030..0032 entry point for 0701
00:0033..0037 unused (?)
00:0038..003a invalid reset vector
00:003b..003f unused (?)
00:0040..0042 function: INTERRUPT_VBLANK
00:0043..0047 unused (?)
00:0048..004a function: INTERRUPT_LCD_STAT
00:004b..0051 function: a *= 64 (rom00:004b)
00:004c..0051 function: a *= 32 (rom00:004c)
00:004d..0051 function: a *= 16 (rom00:004d)
00:004e..0051 function: a *= 8 (rom00:004e)
00:004f..0051 function: a *= 4 (rom00:004f)
00:0050..0051 function: a *= 2 (rom00:0050)
00:0052..0059 function: hl *= 128 (rom00:0052)
00:0053..0059 function: hl *= 64 (rom00:0053)
00:0054..0059 function: hl *= 32 (rom00:0054)
00:0055..0059 function: hl *= 16 (rom00:0055)
00:0056..0059 function: hl *= 8 (rom00:0056)
00:0057..0059 function: hl *= 4 (rom00:0057)
00:0058..0059 function: hl *= 2 (rom00:0058)
00:005a..0062 function: hl = bc + hl * 128 (rom00:005a)
00:005b..0062 function: hl = bc + hl * 64 (rom00:005b)
00:005c..0062 function: hl = bc + hl * 32 (rom00:005c)
00:005d..0062 function: hl = bc + hl * 16 (rom00:005d)
00:005e..0062 function: hl = bc + hl * 8 (rom00:005e)
00:005f..0062 function: hl = bc + hl * 4 (rom00:005f)
00:0060..0062 function: hl = bc + hl * 2 (rom00:0060)
00:0063..006b function: hl = de + hl * 128 (rom00:0063)
00:0064..006b function: hl = de + hl * 64 (rom00:0064)
00:0065..006b function: hl = de + hl * 32 (rom00:0065)
00:0066..006b function: hl = de + hl * 16 (rom00:0066)
00:0067..006b function: hl = de + hl * 8 (rom00:0067)
00:0068..006b function: hl = de + hl * 4 (rom00:0068)
00:0069..006b function: hl = de + hl * 2 (rom00:0069)
00:006c..0071 function: MEMCLEAR
  rom00:006c
    saved:     cde, cf
    inputs:    hl <-- address
                b <-- length
    outputs:   hl <-- address + length
                a <-- 0
                b <-- 0
               zf <-- 0
    notes:
      Clears the specified memory to 0.
      If b is 0, the length is effectively 256.
00:006d..0071 function: MEMSET
  rom00:006d
    saved:     acde, cf
    inputs:    hl <-- address
                b <-- length
                a <-- value
    outputs:   hl <-- address + length
                b <-- 0
               zf <-- 0
    notes:
      Sets every byte in the specified memory to the value.
      If b is 0, the length is effectively 256.
00:0072..007f function: MEMCLEAR_16
  rom00::0072
    saved:     fde
    inputs:    hl <-- address
               bc <-- length
    outputs:   hl <-- address + length
               bc <-- 0
                a <-- 0
    notes:
      Clears the specified memory to 0.
00:0073..007f function: MEMSET_16
  rom00:0073
    saved:     afde
    inputs:    hl <-- address
               bc <-- length
                a <-- value
    outputs:   hl <-- address + length
               bc <-- 0
    notes:
      Sets every byte in the specified memory to the value.
00:0080..0088 function: MEMCOPY
  rom00:0080
    saved:     afc
    inputs:    hl <-- source
               de <-- destination
                b <-- length
    outputs:   hl <-- source + length
               de <-- destination + length
                b <-- 0
    notes:
      Copies memory from the source to the destination.
      If b is 0, the length is effectively 256.
00:0089..0093 function: MEMCOPY_16
  rom00:0089
    saved:     af
    inputs:    hl <-- source
               de <-- destination
               bc <-- length
    outputs:   hl <-- source + length
               de <-- destination + length
               bc <-- 0
    notes:
      Copies memory from the source to the destination.
00:0094..009b function: VRAM_MEMSET
  rom00:0094
    saved:     acde, cf
    inputs:    hl <-- address
                b <-- length
                a <-- value
    outputs:   hl <-- address + length
                b <-- 0
               zf <-- 0
    notes:
      As MEMSET, but safe to use for VRAM.
00:009c..00a3 function: VRAM_MEMSET_16
  rom00:009c
    saved:     afde
    inputs:    hl <-- address
               bc <-- length
                a <-- value
    outputs:   hl <-- address + length
               bc <-- 0
    notes:
      As MEMSET_16, but safe to use for VRAM.
00:00a4..00ab function: VRAM_MEMCOPY
  rom00:00a4
    saved:     afc
    inputs:    hl <-- source
               de <-- destination
                b <-- length
    outputs:   hl <-- source + length
               de <-- destination + length
                b <-- 0
    notes:
      As MEMCOPY, but safe to use for VRAM.
00:00ac..00b4 function: VRAM_MEMCOPY_16
  rom00:00ac
    saved:     af
    inputs:    hl <-- source
               de <-- destination
               bc <-- length
    outputs:   hl <-- source + length
               de <-- destination + length
               bc <-- 0
    notes:
      As MEMCOPY_16, but safe to use for VRAM.
00:00b5..00bb function: MEMCOPY_FROM_BANK
  rom00:00b5
    saved:     fc
    inputs:    hl <-- source
               de <-- destination
                b <-- length
                a <-- bank
    outputs:   hl <-- source + length
               de <-- destination + length
                b <-- 0
    notes:
      As MEMCOPY, but the operation is performed in a different rom bank.
00:00bc..00c2 function: MEMCOPY_16_FROM_BANK
  rom00:00bc
    saved:     f
    inputs:    hl <-- source
               de <-- destination
               bc <-- length
                a <-- bank
    outputs:   hl <-- source + length
               de <-- destination + length
               bc <-- 0
    notes:
      As MEMCOPY_16, but the operation is performed in a different rom bank.
00:00c3..00c9 function: VRAM_MEMCOPY_FROM_BANK
  rom00:00c3
    saved:     fc
    inputs:    hl <-- source
               de <-- destination
                b <-- length
                a <-- bank
    outputs:   hl <-- source + length
               de <-- destination + length
                b <-- 0
    notes:
      As VRAM_MEMCOPY, but the operation is performed in a different rom bank.
00:00ca..00d1 function: VRAM_MEMCOPY_16_FROM_BANK
  rom00:00ca
    saved:     f
    inputs:    hl <-- source
               de <-- destination
               bc <-- length
                a <-- bank
    outputs:   hl <-- source + length
               de <-- destination + length
               bc <-- 0
    notes:
      As VRAM_MEMCOPY_16, but the operation is performed in a different rom bank.
00:00d2..00d8 function: READ_FROM_BANK
  rom00:00d2
    saved:     fbcdehl
    inputs:    hl <-- address
                a <-- bank
    outputs:    a <-- value at address in the given bank
00:00d9..00e5 function: WAIT_FOR_VBLANK
  rom00:00d9, rst 10
  saved:     afbcdehl
  notes:
    Also performs some processing related to OAM animation (which makes characters walk in place).
    This is pretty much always appropriate to do, since it doesn't do the DMA itself.
00:00e6..00f1 data used to initialize RAM programs
00:00f2..00f9 data used to initialize OAM DMA hram program
00:00fa..00ff unused (?)
00:0100..0103 global entry point (jumps to MAIN)
00:0104..014f ROM header
00:0150..01f4 various entry points for other functions in the 0200..18ff region
00:01f5..01ff unused (?)
00:0200..02ef function: MAIN
  rom00:0200
  notes:
    Performs various initialization.
    Runs the top-level MAIN_MENU, then jumps to the appropriate handler.
    Also, jump to this address to restart the game.
00:02f0..0305 function: MULTIPLY_8_8
  rom00:02f0, rom00:0150
    saved:     afbcde
    inputs:     h <-- multiplicand
                l <-- multiplicand
    outputs:   hl <-- product
00:0306..0320 function: DIVIDE_8_8
  rom00:0306, rom00:0153
    saved:     afbcde
    inputs:     h <-- dividend
                l <-- divisor
    outputs:    h <-- quotient
                l <-- remainder
00:0321..033e function: MULTIPLY_16_16
  rom00:0321, rom00:015c
    saved:     afbc
    inputs:    hl <-- multiplicand
               de <-- multiplicand
    outputs:   hl <-- product (high 16 bits)
               de <-- product (low 16 bits)
00:033f..0375 function: DIVIDE_16_16
  rom00:033f, rom00:015f
    saved:     afbc
    inputs:    hl <-- dividend
               de <-- divisor
    outputs:   hl <-- quotient
               de <-- remainder
00:0376..0389 function: SUBTRACT_16_16
  rom00:0376, rom00:0156
    saved:     abcde
    inputs:    hl <-- minuend
               de <-- subtrahend
    outputs:   hl <-- difference
               zf <-- zero
               cf <-- carry
    notes:
      This function uses ff90 as temp storage.
00:038a..038f function: COMPARE_16_16
  rom00:038a, rom00:0159
    saved:     abcdehl
    inputs:    hl <-- minuend
               de <-- subtrahend
    outputs:   zf <-- zero
               cf <-- carry
    notes:
      This function uses ff90 as temp storage.
00:0390..03a5 function: ADD_24_24
  rom00:0390, rom00:0162
    saved:     abcdehl
    inputs:    de <-- address of addend and resulting sum
               hl <-- address of addend
    outputs:   cf <-- carry
    notes:
      This function uses ff90 as temp storage.
00:03a6..03bb function: SUBTRACT_24_24
  rom00:03a6, rom00:0165
    saved:     abcdehl
    inputs:    de <-- address of minuend and resulting difference
               hl <-- address of subtrahend
    outputs:   zf <-- zero
               cf <-- carry
    notes:
      This function uses ff90 as temp storage.
00:03bc..03db function: COMPARE_24_24
  rom00:03bc, rom00:0168
    saved:     abcdehl
    inputs:    de <-- address of minuend
               hl <-- address of subtrahend
    outputs:   zf <-- zero
               cf <-- carry
    notes:
      This function uses ff90 as temp storage.
00:03dc..040a function: MULTIPLY_24_8
  rom00:03dc, rom00:0180
    saved:     afbcdehl
    inputs:    de <-- address of 24-bit multiplicand and resulting 32-bit product
                a <-- multiplicand
00:040b..043d function: DIVIDE_24_8
  rom00:040b, rom00:0183
    saved:     bcdehl
    inputs:    de <-- address of dividend and quotient
                a <-- divisor
    outputs:    a <-- remainder
00:043e..0468 function: RANDOM_INTEGER
  rom00:043e, rom00:016b
    saved:     bcdehl
    inputs:     e <-- minimum integer (inclusive)
                d <-- maximum integer (inclusive)
                a <-- seed index
    outputs:    a <-- (pseudo-)random integer
00:0469..0493 function: PROCESS_BUTTON_PRESS_EVENTS
  rom00:0469, rom00:0171
    saved:     bdehl
    outputs: ff8a <-- button data
    notes:
      Handles "key repeat"-style button processing.
      (The raw button reads are done elsewhere -- generally in the LCD stat interrupt.)
00:0494..049c function: READ_BUTTONS
  rom00:0494, rom00:01a7
    outputs:    a <-- button data
    notes:
      This does some minimal per-frame processing so that it is appropriate to call in a blocking loop.
00:049d..04a5 function: WAIT_FOR_RELEASE
  rom00:049d, rom00:0174
    saved:     afbcdehl
    notes:
      Waits until no buttons are held down.
      In contrast with WAIT_FOR_RELEASE_DMA, no OAM DMA is performed during the wait.
      This means that sprite animations will not update.
00:04a6..04b0 function: WAIT_FOR_RELEASE_DMA
  rom00:04a6
    saved:     afbcdehl
    notes:
      Waits until no buttons are held down.
      In contrast with WAIT_FOR_RELEASE, OAM DMA is performed every frame during the wait.
      This means that sprite animations will update.
00:04b1..04be function: BANK_SWITCH
  rom00:04b1, rom00:01aa
    saved:     fbcdehl
    inputs:     a <-- new rom bank
    outputs:    a <-- old rom bank
    notes:
      Generally, SWITCH_BANK_SAFE is used instead unless interrupts are already disabled.
      The first time this is called after a reset, the return value will not be accurate.
00:04bf..04f3 function: LONG_CALL
  rom00:04bf, rom00:017d
    notes:
      This function allows a function from another rom bank to be called seamlessly.
      The address of the function to call is given inline in the first two bytes after the call to LONG_CALL.
      The bank of the function to call is inline in the third byte after LONG_CALL.
      The return address is properly adjusted to skip these three bytes when returning.
00:04f4..04fa function: SRAM_DISABLE
  rom00:04f4, rom00:01c8
    saved:     afbcdehl
    notes:
      See SRAM_ENABLE. This should be called ASAP after SRAM processing is complete.
00:04fb..0503 function: SRAM_ENABLE
  rom00:04fb, rom00:01cb
    saved:     afbcdehl
    notes:
      Enables access to SRAM. After processing is complete, SRAM_DISABLE must be called.
      Note that interrupts are disabled during while SRAM is enabled.
00:0504..050a function: TEST_BIT
  rom00:0504
    saved:     bcdehl
    inputs:     a <-- number
                c <-- bit index (from least-significant)
    outputs:   zf <-- iff the given bet is 1
00:050b..0511 function: SET_BIT
  rom00:050b
    saved:     fbcdehl
    inputs:     a <-- number
                c <-- bit index (from least-significant) to set
00:0512..0525 function: CLEAR_BIT
  rom00:050b
    saved:     fbcdehl
    inputs:     a <-- number
                c <-- bit index (from least-significant) to clear
00:0526..0532 function: d *= 8, e *= 8
  rom00:0526
    notes:
      Because Gameboy tiles are 8x8, this function can be used to convert tile coordinates into pixel coordinates.
00:0533..0545 function: MENU_CURSOR_STOPS_CLEAR
  rom00:0533, rom00:01b3
    notes:
      Initializes various data related to menu cursor stops.
      This is appropriate to call before setting up new cursor stops.
00:0536..0545 function: MENU_CURSOR_STOPS_CLEAR_KEEP_SELECTION
  rom00:0536, rom00:01b6
    notes:
      As MENU_CURSOR_STOPS_CLEAR, but the current cursor selection variable (ff9b) is preserved.
00:0546..054f function: MENU_CURSOR_CLEAR_POSITION
  rom00:0546, rom00:01b0
    notes:
      Clears the various menu cursor position variables.
      This is appropriate to call before running a new selection program.
00:0550..055c function: LOAD_STANDARD_NPC_GFX
  rom00:0550, rom00:01ef
    notes:
      The standard NPC gfx tiles are loaded into VRAM.
      This consists of the chest tiles and the menu cursor tiles.
00:055d..05d8 function: LOAD_PLAYER
  rom00:055d, rom00:019e
    inputs:     a <-- monster index
             c709 <-- player index
    notes:
      Loads data for the given monster into party data (c200).
      Player index is adjusted according to party order.
00:05d9..05ee function: ADD_PLAYER_OFFSET
  rom00:05d9, rom00:019b
    inputs:    hl <-- address
                a <-- player index (0..4) or (0..7)
    outputs:   hl <-- adjusted address
    notes:
      If the battle flag (ff8b) is set, this will adjust an address from the d000..d0ff region.
      If the battle flag is not set, this will adjust an address from the c200..c21f region.
      Player index is adjusted by party order.
      For example, given hl=c20b, a=1, and standard party order, this will give c22b which is party[1].str.
00:05ef..0607 function: DECODE_PLAYER_INDEX
  rom00:05ef, rom00:01bc
    inputs:     a <-- player index
    outputs:    a <-- adjusted player index
    notes:
      Adjusts the player index based on the party order.
00:0608..0614 function: TEST_SCRIPT_VAR_0
  rom00:0608, rom00:0198
    outputs:   zf <-- zero
    notes:
      Script var 0 is used to store which guest is in the party, so this checks if a guest is present.
00:0615..0620 function: TEST_CHEST_FLAG
  rom00:0615, rom00:018c
    inputs:     a <-- flag index
    outputs:   zf <-- flag
00:0621..062d function: SET_CHEST_FLAG
  rom00:0621, rom00:018f
    inputs:     a <-- flag index
00:062e..063d function: CHEST_FLAG_HELPER
  rom00:062e
    inputs:     a <-- flag index
    outputs:   hl <-- flag address
                c <-- bit index (from least-significant bit)
00:063e..0649 function: GET_SCRIPT_VAR
  rom00:063e, rom00:0192
    inputs:     e <-- script variable index (0..1f)
    outputs:    a <-- value (0..f)
00:064a..0660 function: SET_SCRIPT_VAR
  rom00:064a, rom00:0195
    inputs:     e <-- script variable index (0..1f)
                a <-- value (only lower nybble used)
00:0661..066d function: SCRIPT_VAR_HELPER
  rom00:0661
    inputs:     e <-- script variable index (0..1f)
    outputs:   hl <-- script variable address
                a <-- value at the address (both nybbles)
               cf <-- if set, use upper nybble
00:066e..0678 function: TEST_MEMO_FLAG
  rom00:066e, rom00:01e6
    inputs:     d <-- memo section (0..f)
                e <-- section index (0..f)
    outputs:   zf <-- flag
00:0679..0689 function: MEMO_FLAG_HELPER
  rom00:0679
    inputs:     d <-- memo section (0..f)
                e <-- section index (0..f)
    outputs:   hl <-- flag address
                a <-- value at the address (all 8 bits)
                c <-- bit index (from least-significant bit)
00:068a..068e function: OAM_DMA_STANDARD 
  rom00:068a, rom00:01ec
    notes:
      Performs OAM DMA from either c000 or c100 depending on the currently active animation frame.
      This is appropriate to use if you already know this is the right region to use.
      Sometimes, however, cc00 should be used instead.
      Generally, this is appropriate unless the window is active.
      Note that this doesn't inherently wait for vblank.
00:068f..06af function: WAIT_FOR_VBLANK_AND_OAM_DMA
  rom00:068f
    notes:
      As WAIT_FOR_VBLANK, followed by a smart OAM DMA.
      Unlike OAM_DMA_STANDARD, this will read the various state variables and do the appropriate DMA.
      (It may choose c000, c100, or cc00.)
00:06b0..06f3 function: SETUP_OAM_DMA
  rom00:06b0
    notes:
      This sets up the state variables for OAM DMA.
      It will also copy the appropriate data to cc00 if necessary.
      It is appropriate to call this function once per frame.
      However, it is generally not called directly, since WAIT_FOR_VBLANK calls it.
00:06f4..0700 function: END_BATTLE_MODE
  rom00:06f4, rom00:01f2
    notes:
      Resets some global flags at the end of battle and calls LOAD_STANDARD_NPC_GFX.
00:0701..070b function: READ_SCRIPT_STREAM_BYTE
  rom00:0701, rst 30
    outputs:    a <-- next byte
    notes:
      Reads the next byte from the currently executing script.
      The script pointer will be incremented so next time the next byte will be read.
00:070c..0754 function: EXECUTE_SCRIPT_INSTRUCTION
  rom00:070c
    notes:
      Reads and executes one instruction (which may be multiple bytes) from the current executing script.
      The script pointer will be incremented one past the end of the instruction.
      In the case of an #end instruction, the return address will be adjusted --
      two bytes (e.g. jr) will be skipped at the return point.
00:073b..0754 function: PRINT_TILE
  rom00:073b
    inputs:     a <-- tile index
    notes:
      Prints the given tile to the script window.
00:0755..075d function: SCRIPT_WINDOW_WRITE
  rom00:0755
    inputs:     a <-- tile index
    outputs:   hl <-- next window address
00:075e..078c function: SCRIPT_WINDOW_BEGIN
  rom00:075e
    notes:
      Will draw the frame (if appropriate) and handle line wrapping if needed by the current state.
      This can be called before writing a byte to the window with SCRIPT_WINDOW_WRITE.
00:078d..0795 function: SCRIPT_WINDOW_GET_LINE_START
  rom00:078d
    outputs:   hl <-- line start address (c783)
    notes:
      Gets the address of the start of the current script window line.
00:0796..079e function: SCRIPT_WINDOW_SET_LINE_START
  rom00:0796
    inputs:    hl <-- line start address
    notes:
      Sets the address of the start of the current script window line.
00:079f..07a9 function: SCRIPT_WINDOW_GET_PTR
  rom00:079f
    outputs:   hl <-- current char ptr address (c781)
    notes:
      Gets the address of the next script window tile.
00:07aa..07b4 function: SCRIPT_WINDOW_SET_PTR
  rom00:07aa
    inputs:    hl <-- char ptr address
    notes:
      Sets the address of the next script window tile.
00:07b5..07bd function: GET_SCRIPT_STREAM_PTR
  rom00:07b5
    outputs:   de <-- script stream pointer
00:07be..07c6 function: SET_SCRIPT_STREAM_PTR
  rom00:07be
    inputs:    de <-- script stream pointer
00:07c7..07e8 function: INITIALIZE_SCRIPT_STREAM_PTR
  rom0:07c7
    inputs:    bc <-- address of script offset table
               de <-- script index (see below)
             ffa0 <-- mode
    notes:
      The script stream pointer is set to its initial value based on the requested script.
      The ffa0 mode controls other things as well, but for the purposes of this function:
        00..01 <-- e is script index, d is not used
        02     <-- de - 0x100 is script index
        03     <-- d90c is script index, de is not used
        04..ff <-- e is script index, d is not used
00:07e9..07ff function: INITIALIZE_SCRIPT_STREAM_BANK
  rom00:07e9
    inputs:  ffa0 <-- mode
    notes:
      The bank is switched to the appropriate value for this script mode.
      The old bank is stored in the script stack frame so it can restored later.
      The ffa0 mode controls other things as well, but for the purposes of this function:
        00..01 <-- bank 0b
        02     <-- bank 0a
        03..ff <-- bank 0b
00:0800..08a2 function: EXECUTE_SCRIPT
  rom00:0800, rom00:0020, rst 20
    inputs:    de <-- script identifier (if not in battle)
             d90c <-- script index (if in battle)
    notes:
      The mode (ffa0) is set accordingly:
        00     <-- (different entry point -- see EXECUTE_BOX_SCRIPT)
        01     <-- d is 0
                   scripts from bank 0b:4000 indexed by e
        02     <-- d is 1
                   scripts from bank 0a:4000 indexed by e
                   (this section could be extended to be indexed by de - 0x100 but there aren't that many scripts)
        03     <-- if in battle (de is ignored)
                   scripts from bank 0b:6b80 indexed by d90c
        04     <-- special case for de == 0070 (intro text scroll)
                   script is read normally as in case 01
00:08a3..08a9 function: REVERSE_MEMCOPY
  rom00:08a3
    saved:     c, cf
    inputs:    de <-- source (will read first from previous address)
               hl <-- destination (will write first to this address)
                b <-- length
    outputs:   de <-- source - length
               hl <-- destination - length
                b <-- 0
               zf <-- 0
00:08aa..0900 function: SCRIPT_WINDOW_SCROLL
  rom00:08aa
    notes:
      Moves the text in the window up by one line (operating on VRAM).
      Timing controlled by input as with text printing. The bottom line
      of the window is cleared. (The game's text is mostly double spaced,
      so this will usually happen twice before drawing new text.)
00:0901..0907 function: EXECUTE_BOX_SCRIPT_WITH_OPTIONS
  rom00:0901, rom00:01c5
    inputs:     e <-- box script index
                a <-- options
    notes:
      Options is c7d2:
        01 <-- only write to window memory; don't copy to VRAM
        02 <-- disable frame
00:0908..0915 function: EXECUTE_BOX_SCRIPT
  rom00:0908, rst 08
    saved:      afbcdehl
    inputs:     e <-- box script index
    notes:
      As EXECUTE_BOX_SCRIPT_WITH_OPTIONS with options = 0.
00:0916..0a2a function: EXECUTE_BOX_SCRIPT_HELPER
  rom00:0916
    notes:
      Shared implementation for EXECUTE_BOX_SCRIPT and EXECUTE_BOX_SCRIPT_WITH_OPTIONS.
      Should not be called directly.
00:0a2b..0a5b function: SCRIPT_WINDOW_WRITE_FRAME
  rom00:0a2b, rom00:01ce
    inputs:     b <-- width
                c <-- height
    outputs:   hl <-- script window address after the end of the frame
    notes:
      Writes the tiles for the window frame (border) to the script window.
00:0a5c..0ae1 function: DRAW_BOX_SCRIPT
  rom00:0a5c, rom00:01ad
    notes:
      This is used internally by the box script system to draw the script window to the screen.
00:0ac2..0ae1 function: DRAW_SCRIPT_WINDOW
  rom00:0ac2, rom00:01d1
    inputs:    de <-- vram address to draw to
                b <-- width
                c <-- height
    notes:
      As DRAW_BOX_SCRIPT, but doesn't read state from the box script variables.
      (Instead, the needed data is passed in.)
      Compared to DRAW_TILE_RECTANGLE, this does more work such as setting the ff4a, ff4b window registers.
00:0ae2..0af2 function: BOX_SCRIPT_INITIAL_SCRIPT_WINDOW_PTR
  rom00:0ae2
    outputs:   hl <-- intial script window pointer
00:0af3..0b08 function: DRAW_TILE_RECTANGLE
  rom00:0af3, rom00:0186
    inputs:    hl <-- rectangle address
               de <-- vram address to draw to
                b <-- width
                c <-- height
    notes:
      Copies the given rectangle to a VRAM tile map.
      The script window is a rectangle, so this can be used to draw it.
00:0b09..0b0e static array of window y-offsets
    notes:
      Each entry is two bytes.
      The first byte is y-offset in tiles (c7a4).
      The second byte is y-offset in pixels (c7a5, ff4a)
      Entries are:
        00 <-- battle mode
        01 <-- map mode
        02 <-- full screen window (e.g. start menu)
00:0b0f..0b30 script instruction #exit (00, 0e)
  rom00:0b0f
00:0b31       script instruction #nop (31)
  rom00:0b31
00:0b32..0b3b script instruction #var-inc (12)
  rom00:0b32
00:0b3c..0b45 script instruction #var-dec (13)
  rom00:0b3c
00:0b46..0b4a function READ_SCRIPT_STREAM_SCRIPT_VAR
  rom00:0b46
    outputs:    a <-- value (0..f)
    notes:
      Reads a byte from the script stream (and advances it).
00:0b4b..0b4f script instruction #var-set (14)
  rom00:0b4b
00:0b50..0b5a script instruction #memo-set (18)
  rom00:0b50
00:0b5b..0b65 script instruction #memo-clear (40)
  rom00:0b5b
00:0b66..0b71 function READ_SCRIPT_STREAM_NYBBLES
  rom00:0b66
    outputs:    d <-- high nybble
                e <-- low nybble
    notes:
      Reads a byte from the script stream (and advances it).
00:0b72..0b9a script instruction #item-test (17)
  rom00:0b72
00:0b9b..0bb5 script instruction #stone-test (43)
  rom00:0b9b
00:0bb6..0bcd script instruction #var-test (15)
  rom00:0bb6
00:0bce..0bd7 script instruction #magi-test-count (1a)
  rom00:0bce
00:0bd8..0be3 script instruction #magi-test (1b)
  rom00:0bd8
00:0be4..0bed script instruction #encounter-check (37)
  rom00:0be4
00:0bef..0bf6 script instruction #music-test (44)
  rom00:0bef
00:0bf7..0bff script instruction #defeated-test (45)
  rom00:0bf7
00:0c00..0c0e script instruction #prompt-yes/no (16)
  rom00:0c00
00:0c0f..0c1a script instruction #game-end (4d)
  rom00:0c0f
00:0c1b..0c20 script instruction #teleport-disable (4c)
  rom00:0c1b
00:0c21..0c26 script instruction #memo (4b)
  rom00:0c21
00:0c27..0c8b script instruction #text-save (49)
  rom00:0c27
00:0c8c..0c9d function: GET_CURRENT_SAVE_SLOT_ADDRESS
  rom00:0c8c
    inputs:    bc <-- offset
    outputs:   hl <-- address
    notes:
      The current save slot is determined by c7dd.
00:0c9e..0ca8 script instruction #battle-graphics-swap (48)
  rom00:0c9e
00:0ca9..0cb9 script instruction #prompt (0b)
  rom00:0ca9
00:0cba..0cbf script instruction #npc-refresh (10)
  rom00:0cba
00:0cc0..0ccb script instruction #wait (11)
  rom00:0cc0
00:0ccc..0ccf script instruction #text-raw (07)
  rom00:0ccc
00:0cd0..0ceb script instruction #cursor-text (39)
  rom00:0cd0
00:0cec..0d04 script instruction #cursor (2e)
  rom00:0cec
00:0d05..0d90 script instruction #sprite (08)
  rom00:0d05
00:0d91..0da5 function: GET_STATUS_DISPLAY_PARAMETER
  rom00:0d91
    inputs:     a <-- player index
    outputs:    e <-- status display parameter
    notes:
      The display parameter is as the first byte of >window_sprite_entry & 0xf8.
      Player index is adjusted according to party order.
00:0da6..0daf script instruction #text-right (01)
  rom00:0da6
00:0db0..0db9 script instruction #text-left (02)
  rom00:0db0
00:0dba..0dc6 script instruction #text-up (03)
  rom00:0dba
00:0dc7..0ddd script instruction #text-down (04)
  rom00:0dc7
00:0dde..0dec script instruction #text-space (36)
  rom00:0dde
00:0ded..0e2f script instruction #text-newline-2 (06)
  rom00:0ded
00:0df0..0e2f script instruction #text-newline (05)
  rom00:0df0
00:0e30..0e5b script instruction #window-show (0c)
  rom00:0e30
00:0e5c..0e71 script instruction #window-hide (0d)
  rom00:0e5c
00:0e72..0e9b script instruction #window-draw (0f)
  rom00:0e72
00:0e9c..0ea3 function: SCRIPT_WINDOW_INITIALIZE_MAP_SCRIPT_LINE
  rom00:0e9c
    notes:
      Initializes c77f and c780 to appropriate values for an empty map script line.
      (That is, a line of full width inside of a frame -- 18 tiles.)
00:0ea4..0ea8 unused
    notes:
      This would naturally be the script instruction for #text-hyphen if such a thing existed.
      It doesn't exist, so this appears to be unused code.
      (Hyphen can still be drawn because it is in the alphabet region of the font.)
00:0ea9..0ead script instruction #text-weak (3d)
  rom00:0ea9
00:0eae..0eb2 script instruction #text-resist (3c)
  rom00:0eae
00:0eb3..0eb7 script instruction #text-x (2f)
  rom00:0eb3
00:0eb8..0ec4 script instruction #icon-trash (0a)
  rom00:0eb8
00:0ec5..0f19 script instruction #encounter (09)
  rom00:0ec5
00:0f1a..0f2e script instruction #order-reset (46)
  rom00:0f1a
00:0f2f..0f57 script instruction #guest (38)
  rom00:0f2f
00:0f58..0f5e script instruction #restore (3e)
  rom00:0f58
00:0f5f..0f69 script instruction #heal (3f)
  rom00:0f5f
00:0f6a..0f85 script instruction #gp-subtract (33)
  rom00:0f6a
00:0f86..0f9c script instruction #select-party (34)
  rom00:0f86
00:0f9d..0fad script instruction #select-force (32)
  rom00:0f9d
00:0fae..0fcb script instruction #jukebox (47)
  rom00:0fae
00:0fcc..0fd3 script instruction #text-chomp (20)
  rom00:0fcc
00:0fd4..0ff1 script instruction #text-warp (2d)
  rom00:0fd4
00:0ff2..1031 script instruction #text-name (1f)
  rom00:0ff2
00:1032..1085 script instruction #text-monster (21)
  rom00:1032
00:1086..10b2 script instruction #text-magi (1c)
  rom00:1086
00:10b3..10ba function: GET_PLAYER_MAGI_ADDRESS
  rom00:10b3
    inputs:     a <-- player index
    outputs:   hl <-- address
    notes:
      This will not work for the battle inventory.
      Player index is NOT adjusted by party order.
00:10bb..1152 script instruction #text-item (22)
  rom00:10bb
00:1153..1183 script instruction #text-memo-bank (41)
  rom00:1153
00:1184..11a0 script instruction #memo-box (42)
  rom00:1184
00:11a1..11b9 function: EXECUTE_MEMO_SCRIPT
  rom00:11a1
    inputs:     d <-- memo section
                e <-- section index
00:11ba..11ca function: REMOVE_LAST_MENU_CURSOR
  rom00:11ba
    notes:
      The last added menu cursor will be removed.
      More could still be added later.
      This is not safe to call with no menu cursors.
00:11cb..11d7 script instruction #text-current-hp (24)
  rom00:11cb
00:11d8..11fd script instruction #text-status-or-max-hp (3b)
  rom00:11d8
00:11fe..121d script instruction #text-max-hp (25)
  rom00:11fe
00:121e..124a script instruction #text-magi-count (1d)
  rom00:121e
00:124b..12c1 script instruction #text-item-usage (23, 2b)
  rom00:124b
00:12c2..12c6 script instruction #text-str (27)
  rom00:12c2
00:12c7..12cb script instruction #text-def (28)
  rom00:12c7
00:12cc..12d0 script instruction #text-agl (29)
  rom00:12cc
00:12d1..12db script instruction #text-mana (2a)
  rom00:12d1
00:12dc..12e0 script instruction #text-magi-total (2c)
  rom00:12dc
00:12e1..12f8 script instruction #text-uint8 (26)
  rom00:12e1
00:12f9..1309 script instruction #text-gp-n (35)
  rom00:12f9
00:130a..1312 script instruction #text-gp (1e)
  rom00:130a
00:1313..1377 function: READ_SCRIPT_STREAM_GP_STRING
  rom00:1313
    notes:
      Reads a byte from the script stream (and advances it).
      The byte is interpretted as per #text-gp and #text-gp-n to determine where
      to get the GP number. The resulting string is stored in the string buffer c785.
00:1378..1389 script instruction #text-uint16 (30)
  rom00:1378
00:138a..1399 script instruction #text-monster-count (3a)
  rom00:138a
00:139a..13a7 function: GET_PLAYER_STATUS_BASE_ADDRESS
  rom00:139a
    outputs:   hl <-- address
    notes:
      This will use the battle data status address in battle.
      Otherwise, it will use the party data address.
      Since the base address is returned, it must still be adjusted based on the player index.
00:13a8..13b5 function: GET_PLAYER_MAX_HP_BASE_ADDRESS
  rom00:13a8
    outputs:   hl <-- address
    notes:
      This will use the battle data status address in battle.
      Otherwise, it will use the party data address.
      Since the base address is returned, it must still be adjusted based on the player index.
00:13b6..13bd function: READ_SCRIPT_STREAM_PLAYER_INDEX
  rom00:13b6
    outputs:    a <-- player index
    notes:
      Reads a byte from the script stream (and advances it).
      The byte is interpretted as per the rules of several script functions.
      00..04 are treated as player indices directly, otherwise c709 is used.
      (In the case that c709 is desired, it is conventional to use ff as the script byte.)
      Also, by convention, this index is not yet adjusted for party order.
00:13be..1459 jump table for EXECUTE_SCRIPT_INSTRUCTION
00:145a..1468 array of 24-bit integer constants: #100000, #10000, #1000, #100, #10
00:1469..146b integer constant #999999
00:146c..1476 script instruction #text-accelerate-disable (4a)
  rom00:146c
00:1477..148f function: MENU_YES_NO
  rom00:1477
    outputs:    a <-- 0 is yes, 1 is no
    notes:
      The yes/no prompt is box script 0x12.
      This is meant to be called while running map scripts.
      The state of the map script window and script stack frame will be saved.
00:1490..14c9 script instruction #command (19)
  rom00:1490
00:14ac..14c9 function: EXECUTE_SHOP_COMMAND
  rom00:14ac, rom00:01e9
    inputs:    e <-- shop index
00:14ca..14fe function: MAP_SCRIPT_WINDOW_BEGIN_MENU
  rom00:14ca
    notes:
      The window VRAM is saved (which will only work correctly for the map script VRAM layout).
      The menu cursor information is reset.
      The sprite mode ff96 is set to 2 to allow the cursor sprite to display in the window area.
      The script stack frame and window state are pushed onto the script stack (as SCRIPT_WINDOW_PUSH).
00:14e3..14fe function: SCRIPT_WINDOW_PUSH
  rom00:14e3, rom00:01bf
    notes:
      The script stack frame and window state are pushed onto the script stack.
00:14ff..1537 function: MAP_SCRIPT_WINDOW_END_MENU
  rom00:14ff
    notes:
      This is the reverse of MAP_SCRIPT_WINDOW_BEGIN_MENU.
      Menu cursor information will be cleared, and the cursor sprite will be hidden.
      The script stack and other saved information will be restored.
00:151c..1537 function: SCRIPT_WINDOW_POP
  rom00:151c, rom00:01c2
    notes:
      This is the reverse of SCRIPT_WINDOW_PUSH.
      The script stack frame and window state are popped from the script stack.
00:1538..1548 function: PRINT_INDEXED_NAME_8_ADDR
  rom00:1538
    inputs:    de <-- base address of names (bank f)
               hl <-- address of index
                b <-- length
    notes:
      This is intended to print text, but it can theoretically execute any script data.
      The names are expected to be 8 bytes long, so the length parameter should generally be 8.
      (If it is shorter, the names will be truncated; if longer, they will overflow into the next name.)
00:1539..1548 function: PRINT_INDEXED_NAME_8
  rom00:1539
    inputs:    de <-- base address of names (bank f)
                l <-- index
                b <-- length
    notes:
      As PRINT_INDEXED_NAME_8_ADDR, but the index is stored in l rather than the address hl.
00:153b..1548 function: PRINT_INDEXED_16_NAME_8
  rom00:153b
    inputs:    de <-- base address of names (bank f)
               hl <-- index
                b <-- length
    notes:
      As PRINT_INDEXED_NAME_8, but accepts a 16-bit index. (Names are still 8 bytes.)
00:153e..1548 function: PRINT_NAME
  rom00:153e
    inputs:    hl <-- address of name (bank f)
                b <-- length of name
    notes:
      This is intended to print text, but it can theoretically execute any script data.
00:1549..1579 function: PRINT_SPACES
  rom00:1549
    inputs:     b <-- number of spaces
    notes:
      Prints the given number of spaces (tile 0xff).
00:1557..1579 function: PRINT_BUFFER
  rom00:1557
    notes:
      Prints the text buffer (c785).
      This is intended to print text, but it can theoretically execute any script data.
00:155a..1579 function: EXECUTE_SCRIPT_AT_ADDRESS
  rom00:155a
    inputs:    de <-- address
    notes:
      Executes the script data at the given address.
      This does not fully set up the script stack frame, so it is probably not appropriate to use it unless a script is already running.
      The script will execute in the same context (ffa0 mode, etc) of the outer script.
      However, the outer script pointer is saved and restored.
      Basically, it is "inline" execution.
00:157a..1597 function: COPY_STRING_TO_BUFFER
  rom00:157a
    inputs:    hl <-- string address
    notes:
      The string is copied to the text buffer (c785).
      The string must be terminated with a 0 (and this will also be copied).
      If the chomp flag (c77b) is set, all spaces will be stripped.
      (Yes, all spaces, not just trailing, so don't use for things that have non-trailing/leading spaces.)
00:1598..15b4 function: COPY_NAME_TO_BUFFER
  rom00:1598
    inputs:    hl <-- name address
                b <-- length
    notes:
      The name is copied to the text buffer (c785).
      A 0 is added at the end (which is needed to use EXECUTE_SCRIPT_AT_ADDRESS).
      If the chomp flag (c77b) is set, trailing spaces will be stripped.
00:15b5..15d8 function: NUMBER_16_TO_STRING
  rom00:15b5
    inputs:    hl <-- number
    notes:
      The resulting string is stored in the text buffer (c785) and is terminated with a 0.
      The number will take right-justified in the first 5 characters of the buffer.
      Padding (if needed) is done by adding spaces.
      (Including the 0, the result will have length 6.)
00:15ba..15d8 function: DIGITS_TO_STRING
  rom00:15ba
    inputs:     b <-- length
    notes:
      The text buffer (c785) should be contain the digits.
      Each digit will be converted to the corresponding tile index.
      Prepending zeroes are translated into spaces.
      A 0 is added at the end (so the resulting length is effectively one longer).
00:15d9..15f6 function: NUMBER_16_TO_DIGITS
  rom00:15d9
    inputs:    hl <-- number
      Fills the first 5 places in the text buffer (c785) with the decimal digits of the number.
      (This is then appropriate input for DIGITS_TO_STRING.)
00:15f7..1602 function: NUMBER_16_TO_DIGITS_HELPER
  rom00:15f7
    inputs:    hl <-- number
               bc <-- divisor
    outputs:   hl <-- number % divisor
                a <-- number / divisor
    notes:
      This isn't a good general purpose function since it does division by repeated subtraction.
00:1603..160b function: ITEM_LIST_CONTAINS
  rom00:1603
    inputs:    hl <-- item list address
                c <-- length
                a <-- item to search for
    outputs:   cf <-- if item was missing
    notes:
      The item list should be formatted as two byte entries.
      The first byte should contain the item.
      (The second doesn't matter here, but will generally be the usage.)
      The length is the number of two-byte entries, not the number of bytes.
00:160c..1621 function: LOAD_MONSTER_HALF_SPRITE
  rom00:160c
    inputs:    de <-- vram address where the sprite tiles will go
                a <-- monster index
    notes:
      Generally this will actually be a player sprite.
      Half sprite means it will only load the front and back animation frames.
      (The left and right frames are not used in menus.)
00:1622..1647 function: IS_BATTLE_ITEM_USABLE
  rom00:1622
    inputs:  c709 <-- player index
                b <-- item index
    output:    cf <-- if the given item index is a battle-usable item
    notes:
      This checks the battle inventory.
      Player index is adjusted according to party order.
00:1648..166a function: GET_HIGHEST_STATUS
  rom00:
    inputs:     a <-- status flags
    outputs:    a <-- 0 if no status, or index of status + 1
    notes:
      The input should be a status byte (e.g. c206) where each bit represents a possible abnormal status on the character.
      (See >status.)
      The highest priority status is the one that takes precedence for display purposes.
      Note that this does not correspond to the bit order.
      The status priority is determined in the table at 0f:4238.
      For the return, bit index is from least significant so, e.g. 0 is no status, 1 is Slep, 2 is Para, etc.
00:166b..1673 function: ?TODO?
  rom00:166b, rom00:0189
00:1674..1690 function: VRAM_ENABLE
  rom00:1674, rom00:0177
    notes:
      Enables read/write access to VRAM from the CPU.
      VRAM is only accessible during vblank and hblank, so the CPU should not access it without ensuring this condition.
      This function removes this requirement by setting up the LCD stat interrupt to trigger at the start of each scanline (except during vblank).
      Then the interrupt will wait for hblank before returning.
      Thus, all CPU instructions will be executed during vblank or hblank.
      Since the LCD stat interrupt was replaced, some other features that use a special LCD stat interrupt will be temporarily disabled.
      Also, quite a bit of time is used waiting for hblank, so be sure to turn this off with VRAM_DISABLE as soon as possible.
00:1691..16a7 function: VRAM_DISABLE
  rom00:1691, rom00:017a
    notes:
      Counterpart to VRAM_ENABLE.
      Use this as soon as possible when it is no longer necessary to access VRAM.
00:16a8..16bd function: LCD_STAT_INTERRUPT_VRAM_ACCESS
  rom00:16a8
    notes:
      This is used as the LCD stat (LYC coincidence) interrupt while VRAM access is allowed by VRAM_ENABLE.
      Normal LCD stat processing is still done for LY = 0.
      Otherwise, it will wait for hblank before returning control.
      Before returning, LYC is set to the next line so that the LYC coincidence will happen on every scanline.
      However, the vblank interrupt will reset LYC to 0 which will avoid this being called during vblank.
      (Note that the LY = 0 processing itself will take a few lines.)
00:16be..16cf function: LCD_STAT_INTERRUPT_VRAM_ACCESS_HELPER
  rom00:16be
    notes:
      Helper function for LCD_STAT_INTERRUPT_VRAM_ACCESS called on LY = 0.
      Since the processing may take several scanlines, this function ensures to return on LCD stat mode 3.
      This ensures that when the outer function waits for hblank, it will be the beginning of hblank.
00:16d0..16d8 function: WAIT_FOR_MODE_3
  rom00:16d0
    notes:
      Waits for LCD stat mode 3 (transferring).
      Generally the only important thing about mode 3 is that it is just before hblank.
00:16d9..16de function: LCD_STAT_INTERRUPT_STANDARD
  rom00:16d9
    notes:
      This is the "normal" LCD stat interrupt that is active most of the time during the game.
      It is called once per frame by the LYC = 0 coincidence.
      It calls GAME_UPDATE to do a variety of standard processing.
00:16df..16f8 function: VBLANK_INTERRUPT_STANDARD
  rom00:16df
    notes:
      This is the "normal" vblank interrupt that is active most (all?) of the time during the game.
      LYC is reset to 0 which is important for an interaction with VRAM_ENABLE to allow access during vblank.
      Calls ARSENAL_CLOUD_PROCESS_UPDATE when applicable.
00:16f9..1762 function: GAME_UPDATE
  rom00:16f9, rom00:016e
    notes:
      Performs a variety of actions that need to be done regularly regardless of what else the game is doing.
      Normally, this is done by the LCD_STAT_INTERRUPT_STANDARD to make sure it is called regularly.
      However, it may need to be called manually from the CPU is interrupts are disabled for an extended period or something similar.
      List of what this does:
        Raw input polling
        Audio update
        Soft reset check
        PROCESS_WINDOW_SPRITES (when applicable)
        ARSENAL_CLOUD_STAGE_UPDATE (when applicable)
00:1763..176b function: READ_JOYPAD
  rom00:1763
    inputs:     c <-- must be 0 (otherwise it will read another register)
    outputs:    a <-- joypad buttons result
    notes:
      This should probably not be called directly except from GAME_UPDATE.
      The pattern of reading from ff00 several times in a row is apparently standard practice (mentioned in the Pan Docs).
00:1767..176b function: READ_JOYPAD_2
  rom00:1767
    inputs:     c <-- must be 0 (otherwise it will read another register)
    outputs:    a <-- joypad buttons result
    notes:
      As READ_JOYPAD, but repeats the ff00 read fewer times.
      (The reason for the difference is not clear, probably empircal.)
00:176c..1868 function: PROCESS_WINDOW_SPRITES
  rom00:176c
    notes:
      Stages window sprite data (c7a6) and cursor sprite OAM data to cc00.
      The DMA itself will be done elsewhere by whatever does the vblank wait.
      Sprites will only be processed if they are enabled by ff96.
      For menu cursors, it is the caller's responsibility to check.
      This should be called with interrupts disabled (and usually it is called in an interrupt).
00:1869..1883 function: PROCESS_WINDOW_SPRITES_HELPER
  rom00:1869
    inputs:    hl <-- oam (staging) address
                d <-- y
                e <-- x
    notes:
      Used to load the x and y bytes of into 4-blocks of OAM data.
      (In practice, this will be staged OAM data in cc00, not actual OAM which is written by DMA.)
      Unlike map sprites, left/right wiggle is not included in the template used (4230).
      This is not suitable for generally use because it uses the temp variable ff93 which may be modified by interrupts.
00:1884..18bb LONG_CALL entry points for various functions in other banks
00:18bc..18c2 function: EXECUTE_MENU_CURSOR
  rom00:18bc
    saved:      fbcdehl
    outputs:    a <-- selection index (0xFF if cancel)
    notes:
      Blocks while the player makes a selection (or cancels) based on the menu cursor setup.
00:18c3..18db function: EXECUTE_MENU_CURSOR_WITH_OPTIONS
  rom00:18c3, rom00:01b9
    saved:      afbcdehl
    inputs:     a <-- options
    notes:
      As EXECUTE_MENU_CURSOR, but allows options to be specified.
      Also, the result is not returned. (Actually, there can be more than one, so this is okay.)
      A single result can be retrieved from ff8c if needed.
      For options, the setup is equivalent to c7cd, but the scroll flag (02) will be set automatically for boxscripts which have scrolling flagged.
        01 <-- two cursors
        04 <-- text input mode
00:18dc..18f4 function: EXECUTE_MENU_CURSOR_WITH_OPTIONS (2) (unused?)
  rom00:18dc
    saved:      afbcdehl
    inputs:     a <-- options
    notes:
      This is byte-for-byte an exact duplicate of EXECUTE_MENU_CURSOR_WITH_OPTIONS.
      (Who knows?)
00:18f5..18ff unused (?)
00:1900..1923 various entry points for other functions in the 1900..3fff region
00:1924..1926 unused (?)
00:1927..1928 vector (0,0)
00:1927..1936 vector array of (x,y) pairs used for screen shaking (fx 1)
00:1937..194a offset array for SCREEN_REVERSE_WIPE_WAVY
00:194b..1996 function: UPDATE_SAVE_VARIABLES
  rom00:194b, rom00:1912
    notes:
      Certain saved data (like player x and y) are not stored in the block of RAM that is normally saved.
      In some cases, this is because they are saved in a packed or alternate format.
      In other cases, it seems silly.
      This function moves this data into the saved block of RAM in preparation for a save.
      There is special case to save c305 (upper nybble), the fx script variable.
      This is turned off and backed up in c460 while the player is in the start menu.
      It is restored here so that it will be saved correctly.
00:1997..1a70 function: CONTINUE_GAME
  rom00:1997, rom00:1903
    notes:
      Starts the game when "Continue" is used from the main menu.
      Note that the data from SRAM is assumed to already have been loaded.
      This will do the appropriate setup of all the non-saved variables.
      Then, it will start the GAME_LOOP.
      So, this function will never return.
00:19d6..1a70 function: NEW_GAME
  rom00:19d6, rom00:1900
    notes:
      Starts the game when "Start" is used from the main menu.
      Analogue of CONTINUE_GAME.
00:1a43..1a70 function: GAME_LOOP
  rom00:1a43
    notes:
      This is the top-level game loop.
      It normally loops once each frame while the player is on the map screen.
      Other functions may execute internal loops (such as map scripts, menus, and battle).
      (This isn't really separate from the above functions, but it is important enough to note specially.)
00:1a71..1a72 vector (0,0) (sometimes, this used unified indexing with the following array)
00:1a73..1a7a direction vectors (indexed by >direction)
00:1a7b..1a96 function: START_GAME_HELPER
  rom00:1a7b
    notes:
      This is a helper function for the NEW_GAME/CONTINUE_GAME startup sequence.
      It initializes some variables. It seems to be a rather random assortment.
      I guess this is just stuff that was left out elsewhere.
00:1a97..1ad8 function: APPLY_SCROLLING_AND_FX_AND_OAM_DMA
  rom00:1a97
    notes:
      This function should be called immediately after vblank (WAIT_FOR_VBLANK).
      (This is because it begins with an OAM DMA and modified the scrolling registers.)
      In map mode, it is conventional to do this after every vblank.
      If the fx register is set appropriately, screen shaking will be calculated and applied.
      Then the (earlier computed) scrolling values ffc1 and ffc2 will be applied to the system scrollling registers.
      (This also updates the frame counter for the wave fx.)
00:1ad9..1ae6 function: EXECUTE_DOOR_COMMAND_INLINE
  rom00:1ad9
    notes:
      This is a helper function for GAME_LOOP. This is called if the player lands on a tile with a door command.
      Normally, trigger commands are handled at the end of the game loop, but door commands from triggers are handled specially earlier in the loop.
      This is sort of nice because it stops other things from overriding or interrupting the door transition.
      The prevents some kinds of weirdness like opening the menu (or even saving) on top of a door trigger.
      A more serious problem would be interacting with an NPC on the other side which would actually skip the trigger.
      However, I didn't see any place in the game where this would be able to happen.
      On the other hand, many triggers and actually scripts (even when they appear to be doors), and all this weirdness still applies to script triggers.
      (It doesn't cause any serious problems, that I've found.)
      This function assumes that the current command variable (c442..c443) contains a door command.
00:1ae7..1b36 function: EXECUTE_BATTLE_DOOR_COMMAND
  rom00:1ae7
    notes:
      Executes a map transition according to the battle-door command stored in the command variable c442.
      Battle-doors are rarely used.
      They allow the previous position to be saved without interfering with the return door.
      However, it is hard to restore this position. (Ending a battle will do it.)
00:1aec..1b36 function: EXECUTE_DOOR_COMMAND
  rom00:1aec
    notes:
      Executes a map transition according to the door command stored in the command variable (c442).
00:1b37..1b4a function: CLEAR_STAGED_NPC_OAM
  rom00:1b37
    notes:
      Clears the staged OAM data for NPCs from c000 and c100.
      (Only the player's OAM slot is unaffected.)
      Note that this doesn't do the DMA.
00:1b4b..1b5e function: CLEAR_STAGED_OAM
  rom00:1b4b
    notes:
      Clears all staged OAM data in c000 and c100 including that for the player.
      Note that this doesn't do the DMA.
00:1b5f..1b65 function: RESTORE_MAP_BANK_D
  rom00:1b5f, rom00:1915
    notes:
      Calls RESTORE_MAP, then resets ROM bank to 0x0d. So, this can be called directly
      from bank 0x0d without going through any kind of long call.
00:1b66..1b98 function: EXECUTE_SCRIPT_WITHOUT_FX
  rom00:1b66
    inputs:    de <-- script identifier (if not in battle)
             d90c <-- script index (if in battle)
    notes:
      Works as EXECUTE_SCRIPT, but with special handling for FX.
      Before the script is started, FX will be disabled.
      FX will be restored when the script ends (unless the script changed FX).
00:1b99..1bb8 function: ADD_PLAYER_OFFSET_NON_BATTLE
  rom00:1b99, rom00:1918
    inputs:    hl <-- address
                a <-- player index (0..4)
    outputs:   hl <-- adjusted address
    notes:
      This will adjust an address from the c200..c21f region. (This means h isn't actually affected.)
      Player index is adjusted by party order.
      For example, given hl=c20b, a=1, and standard party order, this will give c22b which is party[1].str.
      Note that this is equivalent to ADD_PLAYER_OFFSET except it does not respect the battle flag.
00:1bb9..1bda function: LOAD_TILE_INFO
  rom00:1bb9
    inputs:     a <-- tileset
    notes:
      Loads the tileset's metadata info into RAM (c520).
00:1bdb..1cae function: LOAD_BATTLE_DOOR
  rom00:1bdb
    notes:
      This is effectively a helper function for EXECUTE_BATTLE_DOOR_COMMAND.
      It performs the operations of loading the actual data from the >door into RAM.
      However, it doesn't follow through with any of the other consequences.
00:1bf0..1cae function: LOAD_DOOR
  rom00:1bf0
    notes:
      This is mostly a helper function for EXECUTE_DOOR_COMMAND.
      It performs the operations of loading the actual data from the >door into RAM.
      It also will setup the return door.
      However, it doesn't follow through with any of the other consequences.
00:1c61..1cae function: LOAD_DOOR_FROM_ADDRESS
  rom00:1c61
    inputs:    hl <-- door address
    notes:
      As LOAD_DOOR, but takes the address directly (so, e.g. a RAM door can be loaded).
      Also, this skips setting up the return door.
00:1caf..1d0c function: LOAD_MAP_HEADER
  rom00:1caf
    notes:
      Loads data from the current map header address (c44b) into RAM.
      Note that the trigger list, npc list, and actual tilemap data are not loaded by this function.
00:1d0d..1d5b function: BATTLE_DOOR_SAVE
  rom00:1d0d
    notes:
      Saves current map position, etc, into the battle door backup space so it can be restored later.
      If a battle door backup is already saved, this operation will be skipped.
00:1d5c..1e20 function: LOAD_VEHICLES
  rom00:1d5c
    notes:
      Loads any vehicles that are saved on the current map into the NPC data (c600).
00:1e21..1ec1 function: REFRESH_PLAYER
  rom00:1e21
    notes:
      Refreshes various player data when loading into a new map.
      Includes speed, animation, sprite index, movements flags, z, OAM, etc.
      Note that this will take into account if the player is currently in a vehicle.
00:1e6c..1ec1 function: REFRESH_PLAYER_TILEMAP_DATA
  rom00:1e6c
    notes:
      As REFRESH_PLAYER, but only performs those updates needed to account for a new tilemap.
      (For example, the player's z might change.) 
00:1ec2..1f19 function: LOAD_PLAYER_OAM_METADATA
  rom00:1ec2
    notes:
      Loads the player's animation type (c431) and OAM template address (c432) based on party data.
      (Vehicle data will be used instead if the player is in a vehicle.)
      This is normally called as part of REFRESH_PLAYER.
00:1f1a..1f22 function: REFRESH_PLAYER_GFX
  rom00:1f1a, rom00:1921
    notes:
      Called when party leader changes. LOAD_PLAYER_GFX, LOAD_PLAYER_OAM_METADATA,
      and STAGE_PLAYER_OAM.
00:1f23..1f54 function: LOAD_PLAYER_GFX
  rom00:1f23
    notes:
      Loads the sprite tiles for the player into VRAM at 8000.
      Note that this can be done independently of vehicles, since vehicles use 8100.
00:1f55..1fa3 function: LOAD_TILE_GFX
  rom00:1f55
    inputs:     a <-- tileset
    notes:
      Loads tile gfx for the given tileset into VRAM at 9000.
      This is a large transfer, so the LCD is actually turned off during this operation.
00:1fa4..1fe9 function: FINISH_MAP_MODE_FRAME
  rom00:1fa4
    notes:
      Waits for vblank and performs various operations appropriate to do at the end of the frame when in map mode.
      (Falls through to UPDATE_SCROLLING_AND_TILE_ANIMATION.)
      In many cases, these operations are slightly modified and/or done piecemeal.
00:1fab..1fe9 function: UPDATE_SCROLLING_AND_TILE_ANIMATION
  rom00:1fab
    notes:
      Updates tile animation, and updates the scrolling (ffc1, ffc2) and player movement variables.
      Generally this is done in vblank at the end of the frame, after APPLY_SCROLLING_AND_FX_AND_OAM_DMA.
      (It doesn't touch video state, so it doesn't have to be done in vblank.)
00:1fea..20c7 function: PLAYER_MOVE
  rom00:1fea
    inputs:     a <-- 0-based >direction
    notes:
      Moves the player. This will generally only take one frame and return.
      The rest of the movement is then carried out according to the state set here.
      (Mostly, this is done in UPDATE_SCROLLING_AND_TILE_ANIMATION.)
      As part of this operation a new row or column of the tilemap is copied into VRAM.
      The direction is dispatched by a jump table at 2007..200e.
00:20c8..20da function: RESET_SCROLLING_AND_DRAW_TILEMAP
  rom00:20c8
    notes:
      Resets the scrolling position to the top left then DRAW_TILEMAP.
00:20db..2103 function: DRAW_TILEMAP
  rom00:20db
    notes:
      Loads map tiles based on the d000 tile map data into the visible portion of the VRAM bg map.
      Note that this takes several frames to finish drawing.
00:2104..212b function: LOAD_TILEMAP_TILE_ROW
  rom00:2104
    inputs:     c <-- x coord of upper left tile
                b <-- y coord of upper left tile
    notes:
      Loads a row of tile data (in VRAM bg map format) to the c400 buffer.
00:212c..2154 function: LOAD_TILEMAP_TILE_COLUMN
  rom00:212c
    inputs:     c <-- x coord of upper left tile
                b <-- y coord of upper left tile
    notes:
      Loads a column of tile data (in VRAM bg map format) to the c400 buffer.
00:2155..2177 function: DRAW_TILEMAP_ROW
  rom00:2155
    inputs:    hl <-- vram address (upper left)
    notes:
      Draws a row of tile data from the c400 buffer to the VRAM bg map.
00:2178..219f function: DRAW_TILEMAP_COLUMN
  rom00:2178
    inputs:    hl <-- vram address (upper left)
    notes:
      Draws a column of tile data from the c400 buffer to the VRAM bg map.
00:21a0..21c1 function: GET_VISIBLE_MAP_TILE
  rom00:21a0
    inputs:     a <-- tile (raw data from the d000 tile map, with flags, etc)
    outputs:    a <-- visible tile -- index in tileset tiles (0..1f)
    notes:
      This accounts for trigger tile lookup, removing flags, and inside/outside.
00:21c2..2231 function: UPDATE_TILE_ANIMATION
  rom00:21ea
    notes:
      The normal entry point is not at the top.
00:2232..223d function: LOAD_TILEMAP
  rom00:2232
    notes:
      Loads tilemap data based on the address stored at c44e (which comes from the map header).
      The result is stored in RAM at d000.
      Note that the tilemap data is stored in a (sort of) compressed form, so this is not a simple copy operation.
00:223e..2252 function: LOAD_TILEMAP_HELPER
  rom00:223e
    inputs:    hl <-- tilemap address (coded as stored in the map header)
    notes:
      This does all the work of LOAD_TILEMAP except the lookup of the data at c44e.
      The significant work that isn't in a child function is the resolution of the "address" to a rom bank and real address.
00:2253..2262 function: LOAD_TILEMAP_TILES
  rom00:2253
    inputs:    hl <-- tilemap stream address
    notes:
      Helper for LOAD_TILEMAP. This decompresses the tilemap data stream.
      The data is basically coded as a stream of "painting" commands like lines, circles and rectangles.
      The dispatch on the command type is done by a jump table at 2263..2278.
      Since helper functions are intermingled with the cases, each case gets a separate entry in this list.
00:2263..2278 tilemap command jump table
00:2279       tilemap command 00 end
00:227a..2292 tilemap command 0a condition
00:2293..22d0 tilemap command 08 flood fill
00:22d1..22fe function: TILEMAP_FLOOD_FILL
  rom00:22d1
    inputs:     c <-- x
                b <-- y
                e <-- flood tile
                d <-- start tile
    notes:
      (Not a true flood fill, but similar idea and simpler to implement.)
00:22ff..231a tilemap command 07 clear
00:231b..2327 tilemap command 01 tile (single)
00:2328..2335 tilemap command 02 set position
00:2336..2381 tilemap command 03 rectangle outline
00:2382..23ad tilemap command 04 rectangle filled
00:23ae..23f8 tilemap command helper small circle (based on table)
00:23f9..2409 function: TILEMAP_SET_SAFE
  rom00:23f9
    inputs:     l <-- x
                h <-- y
             c478 <-- tile
    notes:
      Safe means that x and y will be bounds checked and the store will be skipped if out-of-bounds.
00:240a..24a3 tilemap command 05 circle
00:24a4..24cb function: TILEMAP_MATH_DIVIDE_SIGNED
  rom00:24a4
    saved:     afbcde
    inputs:    hl <-- dividend (signed)
             c477 <-- divisor (unsigned, 1 byte)
    outputs:   hl <-- quotient (signed)
00:24cc..24ea function: TILEMAP_MATH_DIVIDE_UNSIGNED
  rom00:24cc
    inputs:    hl <-- dividend (unsigned)
             c477 <-- divisor (unsigned, 1 byte)
               hl <-- quotient (unsigned)
00:24eb..24fb tilemap command 09 line continued
00:24fc..25d9 tilemap command 06 line
00:25da..25ef function: TILEMAP_SET
  rom00:25da
    inputs:     l <-- x
                h <-- y
                a <-- tile
    notes:
      Contrasted with TILEMAP_SET_SAFE, this performs no bounds checking.
00:25f0..2605 function: TILEMAP_JUSTIFY_RECTANGLE
  rom00:25f0
    inputs:     c <-- x2
                b <-- y2
             c473 <-- x1
             c474 <-- y1
    outputs:    l <-- x (left)
                h <-- y (upper)
                c <-- x (right)
                b <-- y (lower)
    notes:
      Given an arbitrary pair of coordinates that define a rectangle, return the justified coordinate pair.
00:2606..2612 function: TILEMAP_STREAM_LOAD_PAIR
  rom00:2606
    inputs:    hl <-- stream address
               hl <-- next stream address
    outputs:    e <-- first (00..3f)
                d <-- second (in practice, also 00..3f)
    notes:
      Often times a command can take one or two tiles. However, taking one tile is equivalent to two tiles that are the same.
      The stream representation of this construct uses 1 byte for 1 tile and 2 bytes for 2 tiles.
      (The upper bits of the first tile indicate whether there is a second.)
00:2613..2624 function: TILEMAP_STREAM_LOAD_BLOCK
  rom00:2613
    inputs:    hl <-- stream address
    ouputs:    hl <-- next stream address
                e <-- upper 4-bits (usually command index)
               bc <-- lower 12-bits (usually packed x,y)
00:2625..2635 function: TILEMAP_UNPACK_12_6_6
  rom00:2625
    inputs:    bc <-- packed 12-bits (usually x,y)
    outputs:    c <-- lower 6-bits (usually x)
                b <-- upper 6-bits (usually y)
    notes:
      This doesn't read from the stream directly, but the bc is expected to have been read from the stream.
      (bc will usually come from the bc return of TILEMAP_STREAM_LOAD_BLOCK.)
00:2636..2646 function: GET_MAP_TILE
  rom00:2636
    inputs:     c <-- x
                b <-- y
    outputs:    a <-- tile
               bc <-- address of tile in the d000 tile map
    notes:
      By returning the address as well, this can be used as a helper to modify the map.
      The return value is the raw tile from the tile map, so it may be a trigger, and it may have flags.
00:2647..266f function: LOAD_TILEMAP_TRIGGERS
  rom00:2647
    notes:
      Helper for LOAD_TILEMAP.
      After having loaded the data into the tile map at d000, tiles that are flagged as triggers in the map header need to be marked.
      These are replaced in the tilemap with the trigger index flaged with the 0x40 trigger flag.
      The original tile is backed up in the c500 array.
00:2670..26d4 constant data used for drawing small circles
00:26d5..27c8 function: PROCESS_MAP_MODE_DPAD_INPUT
  rom00:26d5
    notes:
      This includes all the logic about whether the player can move.
      It doesn't actually move the player -- it sets the command to f000..f003 which will cause the player to start moving later.
      This also includes the logic for combining player movement with conveyer movement.
00:27c9..28a7 function: PROCESS_MAP_TILE_EVENTS
  rom00:27c9
    notes:
      Handles various events that may happen when the player lands on a new tile.
      (If the player was already on the tile, it will return immediately.)
      The various events are:
        * landing on a trigger tile -- the command will be set, but execution is deferred
        * inside/outside transition
        * conveyer tiles
        * damaging tiles
        * player z change
        * player transparency change
        * random encounters
00:28a8..2938 function: RESTORE_MAP
  rom00:28a8
    notes:
      Called after battle to restore the map.
      This obviously needed to restore all of the video data related to the map, since battle takes over the video system.
      Also, battle repurposes the tile map data (d000), so this data needs to be reloaded.
      If a battle door transition is saved, it will be executed to restore the old map.
      (Much of the logic of EXECUTE_DOOR_COMMAND is duplicated on this path, but it needs to be slightly modified to account for the addition info saved by the battle door.)
00:2939..2945 additional code associated with PROCESS_MAP_TILE_EVENTS
00:2946..2951 function: GET_TRIGGER_MAP_TILE
  rom00:2946
    inputs:     a <-- tile (raw data from the d000 tile map, with flags, etc)
    outputs:    a <-- original tile
    notes:
      Trigger tiles are stored in the d000 tile map in a different format.
      This will find the original tile (saved at c500) and return it.
      Or, if the tile is not a trigger tile, it will be returned.
      The NPC flag will be stripped from the result, but inside/outside will not.
00:2952..2986 function: PROCESS_TILE_DAMAGE
  rom00:2952
    notes:
      Used when the player steps on a damaging tile.
      All characters in the party take 1 damage (but can't drop to 0).
      Also, the player flashes and a sound effect is played.
00:2987..29a2 addition code associated with PROCESS_MAP_TILE_EVENTS
00:29a3..29b2 function: GET_JOYPAD_DIRECTION
  rom00:29a3
    outputs:    a <-- direction (1 + >direction) or 0 if no direction is pressed
00:29b3..29ce function: GET_PLAYER_POSITION_PLUS_OFFSET
  rom00:29b3
    inputs:    hl <-- address of offset vector to add to result
    outputs:    c <-- x (in 16x16 map tiles)
                b <-- y
00:29cf..29e1 function: SET_COMMAND_SAFE
  rom00:29cf
    inputs:    hl <-- command
    notes:
      Here, safe means that if there is already command, nothing will happen.
      The command is set at c442 which is usually processed at the end of GAME_LOOP.
00:29d9..29e1 function: SET_COMMAND
  rom00:29d9
    inputs:    hl <-- command
    notes:
      If there was already a command set, it will be replaced.
      The command is set at c442 which is usually processed at the end of GAME_LOOP.
00:29e2..2a5c function: STAGE_PLAYER_OAM
  rom00:29e2
    inputs:     a <-- direction (>direction)
      Stage player OAM data to c000 and c100.
      Generally the direction should come from c436.
00:2a5d..2ade function: STAGE_OAM_TEMPLATE
  rom00:2a5d
    inputs:    hl <-- destination (typically c0x0, c1x0, or ccx0)
               de <-- oam template address
                c <-- x (in pixels, OAM style, so 0 is offscreen, 16 is top)
                b <-- y (in pixels, OAM style, so 0 is offscreen, 8 is left)
             c43a <-- transparency flags
    notes:
      Loads the OAM template into the OAM staging area with the necessary adjustments for x, y, and transparency.
      This does not include any handling for the tile data fixup, so that should be done by the caller.
00:2adf..2ae5 function: LOAD_RANDOM_ENCOUNTER
  rom00:2adf, rom00:191e
    notes:
      Choose a random encounter according to the current map header data.
      Then load the encounter data into RAM (cfe0).
      This will also perform the screen wipe, but it won't actually run the battle program.
00:2ae6..2b13 function: CHOOSE_RANDOM_ENCOUNTER
  rom00:2ae6
    outputs:    c <-- encounter
    notes:
      Choose a random encounter according to the current map header data.
00:2b14..2b89 function: LOAD_ENCOUNTER
  rom00:2b14, rom00:190f
    inputs:     c <-- encounter
    notes:
      Load the encounter data into RAM (cfe0).
      This will also perform the screen wipe, but it won't actually run the battle program.
00:2b8a..2bed function: SCREEN_WIPE_SCROLL
  rom00:2b8a
    notes:
      As SCREEN_WIPE_DIAMOND, but performs a different wipe effect.
      This one is used e.g. at the end of the intro story sequence (leaving the school).
00:2bee..2c26 function: SCREEN_WIPE_HELPER_INITIALIZE
  rom00:2bee
    notes:
      Similar to SCREEN_SPLIT, but doesn't actually do the split.
      It just clears the window and backs up the scroll registers, etc.
00:2c27..2c94 function LCD_STAT_INTERRUPT_SCREEN_WIPE_SCROLL
  rom00:2c27
    notes:
      LCD stat interrupt used by the SCREEN_WIPE_SCROLL animation.
00:2c95..2cbd function: LCD_STAT_INTERRUPT_WAVE_FX
  rom00:2c95
    notes:
      LCD stat interrupt used to implement the wave FX.
      It does this by modifying the scroll Y register (ff42) on each scanline.
      Note that this interrupt does not perform the normal LCD stat actions (they would take too long).
      Instead, UPDATE_FX (2f7f) will call GAME_UPDATE directly when wave FX is on.
      (UPDATE_FX is conventionally called just before WAIT_FOR_VBLANK wherever FX are applicable.)
00:2cbe..2cff function: SCREEN_WIPE_DIAMOND
  rom00:2cff
    notes:
      Wipes the screen to blank using the diamond (tear) wipe (as the battle transition).
      Most things are restored, but VRAM tilemaps are basically destroyed.
      Staged OAM data is also cleared, and palettes are left turned off.
      Basic mechanics of the effect is that half the map is copied to the window.
      Then window and scroll are animated during hblank.
00:2d00..2d40 function: SCREEN_WIPE_CORNERS
  rom00:2d00
    notes:
      As SCREEN_WIPE_DIAMOND, but performs a different wipe effect.
      This one is used e.g. in the intro story sequence between the bedroom and the downstairs.
00:2d41..2d8e function: SCREEN_REVERSE_WIPE_CORNERS
  rom00:2d41
    notes:
      The wipe-"in" counterpart to SCREEN_WIPE_CORNERS's wipe-"out".
      Actually, the same stat interrupt is used to perform the animation, it is just animated in reverse.
      Unlike wipe-out, wipe-in restores the VRAM tilemaps to the proper state and leaves the palettes on.
      As preconditions, make sure the palettes are all off and the tilemap has been drawn.
00:2d8f..2ddb function: SCREEN_UNSPLIT
  rom00:2d8f
    notes:
      This is a partial counterpart to SCREEN_SPLIT.
      It will restore the portion of the VRAM tilemap from the window back to the bg.
      (This only needs to be done when wiping-in.)
00:2ddc..2e9b function: SCREEN_SPLIT
  rom00:2ddc
    notes:
      Performs a variety of setup needed for both SCREEN_WIPE_DIAMOND and SCREEN_WIPE_CORNERS.
      Primarily, the VRAM bg tilemap is split so that half is moved to the window tilemap.
      Also, fx is turned off and should be restored later from c462.
      The scroll registers are also saved to c47a and c47d to be restored later.
00:2e9c..2ebb function: PUSH_LCD_STAT_LYC_INTERRUPT
  rom00:2e9c
    inputs:    bc <-- address of new LCD stat interrupt
    notes:
      Sets a new stat interrupt and changes the LCD stat interrupt mode to LY=LYC.
      The old interrupt and mode are saved to c473..c476 to be restored later.
      (Only one interrupt can be saved at a time.)
      (This is used to set screen wipe interrupts.)
00:2ebc..2ecd function: SET_LCD_STAT_INTERRUPT_WAVE_FX
  rom00:2ebc
    notes:
      Sets the LCD stat interrupt to LCD_STAT_INTERRUPT_WAVE_FX.
00:2ece..2ee4 function: SET_LCD_STAT_INTERRUPT_STANDARD
  rom00:2ece
    notes:
      Sets the LCD stat interrupt to LCD_STAT_INTERRUPT_STANDARD.
00:2ee5..2eeb function: SCREEN_WIPE_HELPER_RESTORE
  rom00:2ee5
    notes:
      Performs some operations needed to restore state at the end of a screen wipe.
      The LCD stat interrupt previously changed by PUSH_LCD_STAT_LYC_INTERRUPT is restored.
      Also, the scroll registers are restored from c47a, c47d and the window is disabled.
      However, this does not restore fx.
00:2eec..2f02 function: POP_LCD_STAT_LYC_INTERRUPT
  rom00:2eec
    notes:
      Reverses the operation of PUSH_LCD_STAT_LYC_INTERRUPT by restoring the previous interrupt.
00:2f03..2f15 function: SCREEN_WIPE_HELPER_RESTORE_2
  rom00:2f03
    notes:
      Performs some operations needed to restore state at the end of a screen wipe.
      The scroll registers are restored from c47a, c47d and the window is disabled.
00:2f16..2f42 function: LCD_STAT_INTERRUPT_SCREEN_WIPE_DIAMOND
  rom00:2f16
    notes:
      LCD stat interrupt used by the SCREEN_WIPE_DIAMOND animation.
00:2f43..2f7e function: LCD_STAT_INTERRUPT_SCREEN_WIPE_CORNERS
  rom00:2f43
    notes:
      LCD stat interrupts used by the SCREEN_WIPE_CORNERS and SCREEN_REVERSE_WIPE_CORNERS animations.
00:2f7f..2fb0 function: UPDATE_FX
  rom00:2f7f
    notes:
      Performs needed updates for FX based on the fx variable.
      If wave fx is on, this will ensure that LCD_STAT_INTERRUPT_WAVE_FX is set as the LCD stat interrupt.
      In this mode, it will also perform the GAME_UPDATE operation, since this cannot be done in the interrupt (it takes too long).
      Otherwise, the standard interrupt will be restored.
      Because this function is sometimes needed to perform GAME_UPDATE, it is very important to call it once per frame while fx are applicable.
      Conventionally, this is done just before callling WAIT_FOR_VBLANK.
00:2fb1..2fbe function: RESTORE_MAP_SPRITE_GFX
  rom00:2fb1, rom00:191b
    notes:
      Loads player and npc sprite gfx into VRAM.
      During a normal load of a map, these are loaded separately and elsewhere.
      However, when returning from battle, many other things don't need to be reloaded.
      (This is done as part of RESTORE_MAP.)
00:2fbf..3038 function: PROCESS_QUEUED_PLAYER_MOVE
  rom00:2fbf
    notes:
      Processing for queued player movement (c435).
      This is not used when moving the player one tile at a time with the dpad (as normal).
      However, it can be used by script commands that move the player.
00:3039..3182 function: PROCESS_MAP_MODE_BUTTON_INPUT
  rom00:3039
00:3183..31c4 function: GET_NPC
  rom00:3183
    inputs:    e <-- x
               d <-- y
    outputs:  hl <-- address of npc (c7x0)
               a <-- same as l
              cf <-- if no npc
00:31c5..3231 additional code associated with PROCESS_MAP_MODE_BUTTON_INPUT
00:3232..3248 funciton: USE_ITEM_HELPER
  rom00:3232, rom00:190c
    inputs:  cff0 <-- item (low byte)
             cff1 <-- item (high byte)
             cff2 <-- source
             cff3 <-- target
    outputs: cff4 <-- special effect (see USE_ITEM)
    notes:
      Executes the effect when the player uses an item (or magi).
      This should work in either case, but will generally be used for out-of-battle operations.
      However, it uses the same cscript (05) whether in battle or not, but that script has a different path for battle.
      Some special item functions (like warp, or special message boxes) are not handled by this function.
      These special functions (indicated by cff4) are handled by USE_ITEM.
00:3249..32d7 function: PROCESS_NPC_WANDERING
  rom00:3249
00:32d8..333b function: REFRESH_NPC_TILE_INFO
  rom00:32d8
    notes:
      For each active NPC, check the tile they are standing on.
      Ensure the NPC flag is set on the tile.
      Set the NPC transparency and z according to the tile.
00:333c..3389 function: REFRESH_NPCS
  rom00:333c, rom00:1909
    notes:
      Refresh NPCs reloading them from scratch.
      This means their visibility conditions will be checked.
      Various NPC data such as the NPC tile flags, and OAM status will be refreshed/corrected.
00:338a..344a function: LOAD_NPCS
  rom00:338a
    notes:
      Loads NPC data from the map header into RAM (c600).
      Also, load NPC gfx from the map header into VRAM.
      (Some NPCs may fail their visibility conditions and not be loaded.)
      Unused NPC slots will be flagged with 0x80 in the first byte.
00:3397..344a function: LOAD_NPCS_NO_GFX
  rom00:3397
    notes:
      As LOAD_NPCS, but skip loading GFX.
      (They may already be loaded e.g. in the case of NPC_REFRESH.)
00:344b..3629 function: PROCESS_NPCS
  rom00:344b
    notes:
      Processes NPC movement and updates stages NPC OAM data.
      Part of this process is determining which NPCs are visible on screen.
      As part of the movement processing, the equivalent work of REFRESH_NPC_TILE_INFO for NPCs that reach new tiles.
00:362a..363e function: HIDE_NPCS
  rom00:362a
    notes:
      Hides all NPCs by modifying their staged OAM data to appear off-screen.
      Visibility will be restored the next time their staged OAM is updated.
00:363f..367a function: LOAD_NPC_GFX
  rom00:363f
    Load NPC gfx from the map header into VRAM.
    (This doesn't include player gfx or the standard NPC gfx in slot 7.)
00:367b..36d5 function: REFRESH_NPC_STAGED_OAM
  rom00:367b
    inputs:    hl <-- npc address (c6x0)
    notes:
      Reloads OAM data for a particular NPC.
00:36d6..37f5 function: SCREEN_IN_OUT_TRANSITION
  rom00:36d6
    notes:
      Executes the spiral transition effect used when the player's in/out flag changes.
      The in/out flag should be changed to the new value before calling this.
00:37f6..3845 function: SCREEN_IN_OUT_TRANSITION_HELPER_2
  rom00:37f6
    inputs:    hl <-- VRAM address of 8x8 tile to change
                c <-- x (in 8x8 tiles from the upper left of the camera -- counting the off-screen half tile)
                b <-- y (in 8x8 tiles from the upper left of the camera)
                d <-- subtile index (0: top left, 1: top right, 2: bottom left, 3: bottom right)
    notes:
      If the given tile doesn't match the player z, flip it to 1 or 2 as appropriate.
00:3846..388b function: SCREEN_IN_OUT_TRANSITION_HELPER_1
  rom00:3846
    inputs:    hl <-- VRAM address of 8x8 tile to change
                c <-- x (in 8x8 tiles from the upper left of the camera -- counting the off-screen half tile)
                b <-- y (in 8x8 tiles from the upper left of the camera)
                d <-- subtile index (0: top left, 1: top right, 2: bottom left, 3: bottom right)
    notes:
      The same as SCREEN_IN_OUT_TRANSITION_HELPER_2, but it flips tiles that do match to the real tile.
00:388c..391c function: EXECUTE_SCRIPT_COMMAND
  rom00:388c, rom00:1906
    inputs:    hl <-- script stream pointer
    outputs:   hl <-- script stream pointer
    notes:
      This is used to execute most kinds of commands from the script #command instruction.
      However, the special handler is mainly only needed to handled chained NPC and player movement commands.
      This will interpret an entire chain and then execute it simultaneously.
00:391d..3cb3 function: PROCESS_COMMAND
  rom00:391d
    notes:
      If there is a queued command stored at c442 and the player if the player is not moving.
      In any case, a small amount of end of frame processing including WAIT_FOR_VBLANK will occur.
      (Even though the player must not be moving, commands will still trigger if when reaching a new tile because the movement input only happens through a command.)
00:3934..3cb3 function: EXECUTE_COMMAND
  rom00:3934
    inputs:    bc <-- command
    notes:
      Executes the given command.
      This is appropriate for general use.
      Even though the command is passed in, it should also be stored at c442.
00:3cb4..3d7a function: SCREEN_REVERSE_WIPE_WAVY
  rom00:3cb4
    notes:
      This is similar to other fade-ins (such as SCREEN_REVERSE_WIPE_CORNERS).
      The wavy effect is simpler than corners.
      It works by animated the scroll register during the LCD stat interrupt.
      This uses LCD_STAT_INTERRUPT_SCREEN_WIPE_WAVY.
00:3d7b..3d97 function: LCD_STAT_INTERRUPT_SCREEN_WIPE_WAVY
  rom00:3d7b
    notes:
      Stat interrupt used to implement SCREEN_REVERSE_WIPE_WAVY.
00:3d98..3df8 additional code that implements #command.misc.screen-shake
00:3df9..3dff function: SCREEN_SHAKE_HELPER_Y
  rom00:3df9
    inputs:  c474 <-- base scroll y
                c <-- offset
    notes:
      Helper for #command.misc.screen-shake.
      Sets the ff42 scroll Y register to base plus offset.
00:3e00..3e06 function: SCREEN_SHAKE_HELPER_X
  rom00:3df9
    inputs:  c473 <-- base scroll x
                b <-- offset
    notes:
      Helper for #command.misc.screen-shake.
      Sets the ff43 scroll X register to base plus offset.
00:3e07..3e0f shake offset array used by #command.misc.screen-shake
00:3e10..3e2e additional code that implements #command.audio
00:3e2f..3e34 additional code that implements #command.misc.screen-wipe-diamond
00:3e35..3e3a additional code that implements #command.misc.screen-wipe-scroll
00:3e3b..3e45 additional code that implements #command.misc.transition-corners
00:3e46..3e4b additional code that implements #command.misc.screen-wipe-fade
00:3e4c..3e5a function: SCREEN_REVERSE_WIPE
  rom00:3e4c
    notes:
      Performs a reverse wipe or fade-in according to ffc0.
        00  SCREEN_REVERSE_WIPE_FADE
        01  SCREEN_REVERSE_WIPE_CORNERS
        ... SCREEN_REVERSE_WIPE_WAVY (02 is conventional)
00:3e5b..3e95 function: SCREEN_WIPE_FADE
  rom00:3e5b
    notes:
      Not really a wipe, but used in the same way as other wipes.
      This is very straight-forward, it just does palette animation to fade out the screen.
00:3e96..3e9b additional code that implements #command.misc.screen-reverse-wipe-corners
00:3e9c..3ea1 additional code that implements #command.misc.screen-reverse-wipe-fade
00:3ea2..3ede function: SCREEN_REVERSE_WIPE_FADE
  rom00:3ea2
    notes:
      Note really a wipe, but used in the same way as other wipes.
      This is very straight-forward, it just does palette animation to fade in the screen.
00:3edf..3f27 additional code that implements #command.item
00:3f28..3f4d additional code that implements #command.item-force
00:3f4e..3f77 additional code that implements #command.magi
00:3f78..3f86 function: GET_EMPTY_INVENTORY_SLOT
  rom00:3f78
    outputs:   hl <-- slot address
               zf <-- if no slot was empty
    notes:
      Finds the first unused slot in the main inventory (c2b9).
00:3f87..3fbe additional code that implements #command.misc.screen-flash
00:3fbf..3fcf additional code that implements #command.misc.magi-remove
00:3fd0..3ff8 additional code that implements #command.misc.magi-restore
00:3ff9..3fff unused (?)
01:5000..5038 various entry points for other functions in the 5000..6fff region
01:5039..518e function: EXECUTE_MENU_CURSOR_INTERNAL
  rom01:5039, rom01:500c
    notes:
      Generally, this shouldn't be called directly.
      Instead, use one the of the bank 1 wrappers: EXECUTE_MENU_CURSOR, EXECUTE_MENU_CURSOR_WITH_OPTIONS.
01:518f..51f7 function: EXECUTE_TEXT_ENTRY_CURSOR
  rom01:518f
    notes:
      Similar to EXECUTE_MENU_CURSOR, but the cursor theoretically supports scrolling in the X direction in addition to Y.
      Also, the start and select buttons are handled.
      The result is stored in ff8c as usual.
      As usual, 0xff indicates b (back). 0xfe indicates start and 0xfd indicates select.
01:51f8..5226 function: SET_SCROLLING_CURSOR_COORDINATES
  rom01:51f8
    inputs:     c <-- cursor stop x
                b <-- cursor stop y
    notes:
      Sets the cursor coordinates (ff97, ff98) based on the input.
      This may also use the fact that ff9b is the current cursor.
      If the current cursor coordinates are out of the scrolling viewport, the scrolling will be adjusted.
      Even if the cursor is inside the scrolling viewport, the coordinates will be adjusted to account for the scrolling viewport position.
      (This also works correctly for a non-scrolling cursor.)
01:5227..5241 function: CURSOR_CHECK_SCROLL_X
  rom01:5227
    inputs:     c <-- x
    outputs    cf <-- is out of bounds (needs to scroll)
    notes:
      If the cursor is not in a scrolling box, the check will succeed (cf = 0).
      Note that this is not completely parallel to CURSOR_SCROLL_Y, less of the inital work and checking is done.
      (CURSOR_SCROLL_Y should be called first.)
01:5242..5270 function: CURSOR_SCROLL_X
  rom01:5242
    inputs:     c <-- cursor x (adjusted for scrolling offsets)
    outputs:    c <-- updated cursor x (adjusted for scrolling offsets)
    notes:
      Assumes that scrolling should take place (as checked by CHECK_CURSOR_SCROLL_X).
      The scrolling is applied to c and to c797.
01:5271..527b function: MENU_MEMORY_LOAD
  rom01:5271
    inputs:     a <-- menu index
    notes:
      Loads the data from menu memory at the given index into the current menu state (ff9b, c798).
01:527c..5286 function: MENU_MEMORY_SAVE
  rom01:527c
    inputs:     a <-- menu index
    notes:
      Saves the current menu state (ff9b, c798) to menu memory at the given index.
01:5287..528d function: MENU_MEMORY_CLEAR
  rom01:5287
    inputs:     a <-- menu index
    notes:
      Clears the menu memory at the given index.
01:528e..5293 function: GET_MENU_MEMORY_ADDRESS
  rom01:528e
    inputs:     a <-- menu index
    outputs:   hl <-- address
01:5294..52a5 function: SET_SCROLLING_Y_CURSOR_COORDINATES
  rom01:5294
    inputs:     c <-- cursor stop x
                b <-- cursor stop y
    notes:
      As SET_SCROLLING_CURSOR_COORDINATES, but only considers scrolling in the Y coordinate.
      (Both coordinates can still be adjusted.)
01:52a6..52ce function: UPDATE_FIRST_CURSOR_POSITION
  rom01:52a6
    notes:
      If the menu is in two-cusor mode, and the second cursor is active, the first cursor still needs to be drawn.
      This updates the position of the first cursor based on the current scrolling viewport.
01:52cf..52db function: ADD_MENU_SCROLL_OFFSETS
  rom01:52cf
    inputs:     c <-- x
                b <-- y
    outputs:    c <-- x (adjusted)
                b <-- y (adjusted)
    notes:
      Adjust the coordinates by the scroll viewport offsets (c797, c798).
01:52dc..5304 function: CHECK_CURSOR_SCROLL_Y
  rom01:52dc
    inputs:     a <-- cursor index
                c <-- cursor stop x
                b <-- cursor stop y
    outputs:    c <-- cursor x (adjusted by scrolling offsets)
                b <-- cursor y (adjusted by scrolling offsets)
               cf <-- is out of bounds (needs to scroll)
    notes:
      If the cursor is not in a scrolling box, the check will succeed (cf = 0).
01:5305..5326 function: CURSOR_SCROLL_Y
  rom01:5305
    inputs:     b <-- cursor y (adjusted for scrolling offsets)
    outputs:    b <-- updated cursor y (adjusted for scrolling offsets)
    notes:
      Assumes that scrolling should take place (as checked by CHECK_CURSOR_SCROLL_Y).
      The scrolling is applied to b and to c798.
01:5327..5333 function: MOVE_CURSOR_DOWN
  rom01:5327
    outputs:   zf <-- if the cursor changed
    notes:
      Moves the cursor down.
      Out of all the cursor stops with the same x-coordinate and greater y-coordinate as the current stop, this will choose the stop with the smallest y.
      If there is no such cursor, nothing will change.
      If the cursor is changed, it will be reflected in ff9b.
01:5334..5369 function: FIND_CURSOR_DOWN
  rom01:5334
    intputs:     c <-- x
                 b <-- y
    outputs:     a <-- cursor index
                 c <-- found cursor x
                 b <-- found cursor y
                 d <-- 0xff if found, 0 if not found
    notes:
      Finds the appropriate cursor according to the rules of MOVE_CURSOR_DOWN.
01:536a..537c function: MOVE_CURSOR_RIGHT
  rom01:536a
    outputs:   zf <-- if the cursor changed
    notes:
      Moves the cursor right.
      Out of all the cursor stops with the same y-coordinate and greater x-coordinate as the current stop, this will choose the stop with the smallest x.
      If there is no such cursor, nothing will change.
      If the cursor is changed, it will be reflected in ff9b.
01:537d..53b0 function: FIND_CURSOR_RIGHT
  rom01:537d
    intputs:     c <-- x
                 b <-- y
    outputs:     a <-- cursor index
                 c <-- found cursor x
                 b <-- found cursor y
                 d <-- 0xff if found, 0 if not found
    notes:
      Finds the appropriate cursor according to the rules of MOVE_CURSOR_RIGHT.
01:53b1..53bd function: MOVE_CURSOR_UP
  rom01:53b1
    outputs:   zf <-- if the cursor changed
    notes:
      Moves the cursor up.
      Out of all the cursor stops with the same x-coordinate and lesser y-coordinate as the current stop, this will choose the stop with the greatest y.
      If there is no such cursor, nothing will change.
      If the cursor is changed, it will be reflected in ff9b.
01:53be..53f3 function: FIND_CURSOR_UP
  rom01:53be
    intputs:     c <-- x
                 b <-- y
    outputs:     a <-- cursor index
                 c <-- found cursor x
                 b <-- found cursor y
                 d <-- 0xff if found, 0 if not found
    notes:
      Finds the appropriate cursor according to the rules of MOVE_CURSOR_UP.
01:53f4..5406 function: MOVE_CURSOR_LEFT
  rom01:53f4
    outputs:   zf <-- if the cursor changed
    notes:
      Moves the cursor left.
      Out of all the cursor stops with the same y-coordinate and lesser x-coordinate as the current stop, this will choose the stop with the greatest x.
      If there is no such cursor, nothing will change.
      If the cursor is changed, it will be reflected in ff9b.
01:5407..543a function: FIND_CURSOR_LEFT
  rom01:5407
    intputs:     c <-- x
                 b <-- y
    outputs:     a <-- cursor index
                 c <-- found cursor x
                 b <-- found cursor y
                 d <-- 0xff if found, 0 if not found
    notes:
      Finds the appropriate cursor according to the rules of MOVE_CURSOR_LEFT.
01:543b..544f function: FIND_CURSOR_STOP_COORDINATE
  rom01:543b
    inputs:    hl <-- search address
                a <-- search value
    outputs:   hl <-- found address
                a <-- found value (or 0xff if not found)
    notes:
      The search address should be part of the cursor stops array.
      Depending on the alignment of hl, this will search for either matching x (even) or y (odd).
01:5450..5469 function: GET_CURSOR_STOP_COORDINATES
  rom01:5450
    outputs:    c <-- x
                b <-- y
    notes:
      Returns the cursor position from the cursor stops array (c380 or d500).
      The index is given by the current selection (ff9b).
01:546a..5470 function: SET_CURSOR_COORDINATES
  rom01:546a
    inputs:     c <-- x
                b <-- y
    notes:
      Sets the cursor position (ff97, ff98).
01:5471..54c7 function: START_MENU
  rom01:5471, rom01:5000, rom00:1884, rom00:01a1
    notes:
      Executes the start menu (Abil, Item, Equip, ...)
      There are a lot of helper functions for the start menu, so other blocks of code associated with this menu will be noted.
01:54c8..54f7 START_MENU -- Save
01:54f8..5572 START_MENU -- Abil
01:5573..55cf START_MENU -- Item
01:55d0..55d5 function: GET_INVENTORY_SLOT_ADDRESS
  rom01:55d0
    inputs:     a <-- slot index
    outputs:   hl <-- address
01:55d6..55fc additional code associated with START_MENU -- Item
01:55fd..5704 START_MENU -- Equip
01:5705..5715 function: MOVE_ITEM_SLOT_TO_BACKUP_FROM_EQUIPMENT_SLOT_FROM_MENU_MEMORY_8
  rom01:5705
    notes:
      The equipment slot is pulled from memory memory slot 8.
      During equip operations, this slot is the equipment screen (so this is the item to be unequipped).
      The backup slot is at c7db.
01:5716..5724 function: MOVE_ITEM_SLOT_FROM_BACKUP_TO_EQUIPMENT_SLOT_FROM_MENU_MEMORY_8
  rom01:5716
    saved:     af, ?
    notes:
      Reverse operation of MOVE_ITEM_SLOT_TO_BACKUP_FROM_EQUIPMENT_SLOT_FROM_MENU_MEMORY_8.
01:5725..572d addtional code associated with START_MENU -- Equip
01:572e..57db function: EQUIP_ITEM
  rom01:572e
    inputs:  c709 <-- player index
             ff91 <-- item
             c78d <-- >item_data
    outputs: ff9e <-- swap item flags
             ff9f <-- non-zero if invalid
    notes:
      Process all the stat changes associated with equipping the item.
      The item will not be added to the equipment by this function.
      The 01 flag will be set if the player is a robot (to indicate that the usage should maybe be halved later).
      This will also ensure that the player can equip the given item (no slot conflict, not a monster).
      If the equip is not allowed, stats won't change and PLAY_INVALID_SOUND is called.
01:57dc..57f7 additional code associated with START_MENU -- Equip
01:57f8..58ab function: UNEQUIP_ITEM
  rom01:57f8
    inputs:  c709 <-- player index
             ff90 <-- item
             c785 <-- >item_data
    outputs: ff9e <-- swap item flags
    notes:
      Process all the stat changes associated with unequipping the item.
      The item will not be removed from equipment by this function.
      The 02 flag will be set if the player is a robot (to indicate that the usage should maybe be halved later).
01:58ac..590b function: CHECK_AND_SWAP_ITEMS
  rom01:58ac
    inputs:  ff9f <-- non-zero if invalid
             ff9e <-- flags
    notes:
      The items to swap are loaded from menu memory slots 7 and 8 (which are respectively inventory and equipment during Equip processing).
      Slot 7 must correspond to the inventory for this function to work properly.
      The first check is against ff9f which assumes that EQUIP_ITEM was called to set this value appropriately.
      If this check is passed, the confirm sound is played.
      Then, if the inventory slot is the trash, the other item will be deleted and no swap will take place.
      Otherwise it will swap as SWAP_ITEMS.
01:58cd..590b function: SWAP_ITEMS
  rom01:58cd
    inputs:    de <-- item slot 1 address
               hl <-- item slot 2 address
             ff9e <-- flags
    notes:
      Swaps the given items slots.
      (They may be in the same item list, or in different lists.)
      When using this to (un)equip items, de should be the equipped slot and hl should be the inventory slot.
      This includes the logic that halves item usage for robots (and destroys empty items that go to the inventory).
      This does not include logic that changes stats.
      ff9e flags meaning
        01 halve usage on the equipped item (item slot 2)
        02 halve usage on the unequipped item (item slot 1)
01:590c..5918 function: GET_ITEM_ROBOT_NO_REDUCE_FLAG
  rom01:590c
    inputs:     a <-- item
    outputs:   zf <-- flag
    notes:
      The robot no reduce flag indicates that the item usage should not change when a robot equips or unequips.
      This should be set on items with unlimited usage.
      It is also conventionally set on martial arts.
      Such items are not restored when a robot stays at the inn.
01:5919..59e5 START_MENU -- MAGI
01:59e6..5a3b function: MAGI_SELECT_PLAYER_MENU
  rom01:59e6
01:5a3c..5a52 function: MAGI_MENU_START
  rom01:5a3c
    inputs:  c709 <-- player index
    notes:
      Draws all the boxes for the MAGI menu.
      The Equip/Use box is executed last so it's cursor selection is ready to run.
01:5a53..5a5e function: UNEQUIPPED_MAGI_BOX
  rom01:5a53
01:5a5f..5a6b function: MAGI_SUMMARY_BOX
  rom01:5a5f
    inputs:  c709 <-- player index
    notes:
      Displays the box on the MAGI screen that shows the MAGI equipped by the currently selected player.
01:5a6c..5a7e function: MAGI_PLAYER_SELECT_BOX
  rom01:5a6c
01:5a7f..5a8e function: GET_MAGI_LIST_VALUE
  rom01:5a7f
    inputs:  c7e0 <-- list
                a <-- index
    outputs:   hl <-- address
                a <-- value
01:5a84..5a88 function: GET_MAGI_LIST_ADDRESS
  rom01:5a84
    inputs:  c7e0 <-- list
                a <-- index
    outputs:   hl <-- address
01:5a89..5b25 START_MENU -- Memo
01:5b26..5b4e function: SET_EQUIPPED_MAGI
  rom01:5b26
    notes:
      Sets the equipped MAGI in the magi array (c2da) based on the >player info MAGI values.
01:5b4f..5b55 function: GET_MAGI_LIST_BYTE_1
  rom01:5b4f
    inputs:     a <-- magi list index
    outputs:   hl <-- address
                a <-- value
01:5b56..5b94 function: REFRESH_EQUIPPED_MAGI
  rom01:5b56
    notes:
      Refreshes each >player MAGI based on the magi array (c2da).
      Any MAGI that are not equipped are added to the c7e0 list.
01:5b95..5b9c function: GET_PLAYER_MAGI_ADDRESS
  rom01:5b95
    inputs:     a <-- player index
    outputs:   hl <-- address
    notes:
      Player index is NOT adjusted by party order.
01:5b9d..5bac function: REDRAW_START_MENU
  rom01:5b9d
    notes:
      Redraws all the menu boxes needed by the start menu.
      (The start menu doesn't use this to draw itself when it is first opened.)
01:5bad..5bc5 function: PLAYER_SELECT_MENU
  rom01:5bad
    outputs:    a <-- player index
               zf <-- if the menu was canceled
    notes:
      This is the player selection that happens on the main part of the start menu.
      The player index is the raw output of the menu, so it still must be adjusted for party order.
01:5bc6..5d1c function: USE_ITEM
  rom01:5bc6
    inputs:  c709 <-- player index
             ff9d <-- item slot index (in equipment or inventory)
             cff0 <-- item (low byte)
             cff1 <-- item (high byte)
    notes:
      Uses an item from the menu.
      This includes handling any submenus including things like target selection or more specialized functions like warp selection.
      If c709 is 0xff, the main inventory will be used.
      Part of the logic is handled by USE_ITEM_HELPER.
      The cff4 return of USE_ITEM_HELPER can indicate several special item functions:
        00 <-- ?TODO?
        01 <-- nothing happened message
        02 <-- warp
        03 <-- tent
        04 <-- prism
        05 <-- str up message
        06 <-- agi up message
        07 <-- mana up message
        08 <-- hp up message
        .. <-- (invalid script?)
      In some cases (warp), this function may not return normally.
      The stack pointer may be restored from the value saved at c7d7 in order to completely exit the menu.
      Note that if the high byte is non-zero, the item can't really be in a slot, so this function can't be used correctly.
      However, the high byte must still be set to zero (ie it is used).
01:5bcd..5d1c function: USE_ITEM_SKIP_USAGE_CHECK
  rom01:5bcd
    inputs:  c709 <-- player index
             cff0 <-- item (low byte)
             cff1 <-- item (high byte)
    notes:
      As USE_ITEM, but skip the initial usage check.
      This is appropriate to use for MAGI.
      (The item slot index could theoretically be used for non-MAGI, since REDUCE_ITEM_USAGE can still be called.)
01:5d1d..5d4b function: REDUCE_ITEM_USAGE
  rom01:5d1d
    inputs:  c709 <-- player index
             ff9d <-- item slot index (in equipment or inventory)
             cff1 <-- item (high byte)
    notes:
      If c709 is 0xff, the main inventory will be used.
      The usage will not be reduced if it is unlimited (0xfe).
      If the high byte is non-zero, the usage will not be reduced.
      (This is because two-byte items can't exist in equipment or inventory.)
      If the low byte is needed, it will be read directly from the equipment or inventory, not cff0.
      If the item usage falls to zero, it may be deleted.
      Monster and robot items will never be deleted.
      Human and inventory items are always deleted.
      Mutant items in 00..7f are deleted, 80..ff are not.
01:5d4c..5d61 function: GET_ITEM_SLOT_USAGE
  rom01:5d4c
    inputs:  c709 <-- player index
             ff9d <-- item slot index (in equipment or inventory)
    outputs    hl <-- usage address
                a <-- usage
    notes:
      If c709 is 0xff, the main inventory will be used.
      Otherwise, the given player's (non-battle) equipment will be used.
01:5d62..5d66 function: GET_EQUIPMENT_SLOT_ITEM_FROM_MENU_MEMORY_8
  rom01:5d62
    inputs:  c709 <-- player index
    outputs:   hl <-- equipment slot address
                a <-- item (*hl)
    notes:
      The item index is pulled from menu memory slot 8.
      This is the menu memory slot that is used for the player's equipment screen during the Equip menu processing.
01:5d67..5d77 function: GET_EQUIPMENT_SLOT_ADDRESS_FROM_MENU_MEMORY_8
  rom01:5d67
    inputs:  c709 <-- player index
    outputs:   hl <-- address
    notes:
      As GET_EQUIPMENT_SLOT_ADDRESS, but the item index is pulled from menu memory slot 8.
      This is the menu memory slot that is used for the player's equipment screen during the Equip menu processing.
01:5d6d..5d77 function: GET_EQUIPMENT_SLOT_ADDRESS
  rom01:5d6d
    inputs:  c709 <-- player index
                a <-- item index
    outputs:   hl <-- address
01:5d78..5d7c function: GET_INVENTORY_SLOT_ITEM_FROM_MENU_MEMORY_7
  rom01:5d78
    outputs:   hl <-- address
                a <-- item (*hl)
    notes:
      The item index is pulled from menu memory slot 7.
      This is the menu memory slot that is used for the inventory screen during the Equip menu processing.
01:5d7d..5d88 function: GET_INVENTORY_SLOT_ADDRESS_FROM_MENU_MEMORY_7
  rom01:5d7d
    outputs:   hl <-- address
    notes:
      The item index is pulled from menu memory slot 7.
      This is the menu memory slot that is used for the inventory screen during the Equip menu processing.
01:5d89..5da4 function: UNUSED (?)
  rom01:5d89
    inputs:     a <-- item
    outputs:   hl <-- player str address
                c <-- item byte 5
                b <-- item byte 2
    notes:
      This looks like it could have been a helper function for equip stat change handling, but it doesn't appear to be used.
01:5da5..5daf function: READ_BANK_C_DATA_8
  rom01:5da5
    inputs:    de <-- base address
                a <-- index
    outputs:    a <-- data
    notes:
      Reads a byte of data from bank c at address de + 8 * a.
      This can be used to read item data.
01:5db0..5dbf function: GET_ITEM_DATA
  rom01:5db0
    inputs:     a <-- item
               de <-- destination
    outputs:   de <-- destination + 8
    notes:
      Reads the full 8-byte >item_data to address de.
01:5dc0..5df7 function: CHECK_ARMOR_SLOTS
  rom01:5dc0
    inputs:  c709 <-- player index
             c78d <-- new item >item_data
    outputs:   cf <-- if there is a conflict
    notes:
      The item data should be stored at c78d..c794, but only c78f is actually needed.
      Checks if there is a armor conflict slot in equipping the new item (e.g., two helmets can't be equipped).
      This doesn't check the player race -- robots should not call this function at all (or ignore the result).
01:5df8..5e0c function: MENU_INITIALIZE_BLANK
  rom01:5df8
    notes:
      Does pretty much all the standard initialization needed for displaying a fullscreen window (ie executing a box script).
      This will leave the palettes off, hence the "BLANK".
      This allows all the boxes to be invisible while drawing and then be shown simultaneously.
01:5e0d..5e24 function: MENU_INITIALIZE_NORMAL
  rom01:5e0d
    notes:
      Very similar to MENU_INITIALIZE_BLANK.
      However, it will CLEAR_GB_WIN_LIGHT_GRAY.
      Also, the palettes will be left on (which means the user can see the drawing).
01:5e25..5e2a function: CLEAR_WINDOW_SPRITES_AND_CURSOR_STOPS
  rom01:5e25
    notes:
      Clears sprite data as CLEAR_WINDOW_SPRITES.
      Clears cursor stops as MENU_CURSOR_STOPS_CLEAR.
01:5e2b..5e30 function: CLEAR_WINDOW_SPRITES_AND_CURSOR_STOPS_KEEP_SELECTION
  rom01:5e2b
    notes:
      Clears sprite data as CLEAR_WINDOW_SPRITES.
      Clears cursor stops as MENU_CURSOR_STOPS_CLEAR_KEEP_SELECTION.
01:5e31..5e3f function: CLEAR_WINDOW_SPRITES_CURSOR_STOPS_AND_SCROLL (2)
  rom01:5e31
    notes:
      Alternate entry point for CLEAR_WINDOW_SPRITES_CURSOR_STOPS_AND_SCROLL.
      It should do exactly the same thing.
      (It looks like maybe someone thought that the other entry point didn't clear c798 and added this to do that.
      However, both functions will clear c798.)
01:5e35..5e3f function: CLEAR_WINDOW_SPRITES_CURSOR_STOPS_AND_SCROLL
  rom01:5e35
    notes:
      Clears window sprite data as CLEAR_WINDOW_SPRITES.
      Clears cursor stops as MENU_CURSOR_STOPS_CLEAR.
      Also clears the cursor scroll offsets (c797, c798).
01:5e40..5e59 function: CLEAR_WINDOW_SPRITES
  rom01:5e40
    notes:
      Clears window sprite data including menu cursor positions and staged OAM data.
01:5e5a..5e61 function: MENU_MEMORY_CLEAR_ALL
  rom01:5e5a
    notes:
      Clears all menu memory used by non-battle menus. (c34a..c353)
      (I think nothing will wipe out remembered battle selections.)
01:5e62..5e76 function: DRAW_CHARACTERS_MENU
  rom01:5e62
    notes:
      Draws the characters menu (as used e.g. on the main screen of the start menu).
      If there is a guest, the associated guest box will also be drawn.
01:5e65..5e76 function: DRAW_BOX_AND_GUEST_BOX
  rom01:5e65
    inputs:     e <-- box
                d <-- guest box
    notes:
      Draw the box. Then, only if there is a guest, draw the guest box.
      Also performs some initialization:
        MENU_CURSOR_STOPS_CLEAR_KEEP_SELECTION
        Clears menu selection and sprite count.
01:5e68..5e76 function: DRAW_BOX_AND_GUEST_BOX_KEEP_SELECTION
  rom01:5e68
    inputs:     e <-- box
                d <-- guest box
    notes:
      As DRAW_BOX_AND_GUEST_BOX, but don't clear menu selection.
01:5e6c..5e76 function: DRAW_BOX_AND_GUEST_BOX_KEEP_SELECTION_AND_SPRITES
  rom01:5e6c
    inputs:     e <-- box
                d <-- guest box
    notes:
      As DRAW_BOX_AND_GUEST_BOX, but don't clear menu selection or sprite count.
      (Sprites are still cleared either way.
      The reason for leaving the sprite count is that this can be used to load sprites into the later memory slots first.)
01:5e77..5eb0 function: LOAD_MISC_TILES_WITH_BACKUP
  rom01:5e77
    notes:
      Same as LOAD_MISC_TILES, except will backup some of the VRAM tile data if not in battle.
      Tiles from 9600..97ff are backed up in unused portions of the tilemap data before 9700..97ff is replaced.
01:5ea3..5eb0 function: LOAD_MISC_TILES
  rom01:5ea3
    notes:
      Loads standard misc tiles from bank 4:4700 into the last 0x100 bytes of tiles.
01:5eb1..5edb function: EXIT_MENU
  rom01:5eb1
    notes:
      Clear out menu window information, restore backup tiles by LOAD_BACKUP_TILES.
      Also, restore palettes from c700.
01:5eb4..5edb function: EXIT_MENU_NOW
  rom01:5eb4
    notes:
      As EXIT_MENU, but don't wait for input.
01:5edc..5efd function: LOAD_BACKUP_TILES
  rom01:5edc
    notes:
      Restore the tile data previously backed-up by LOAD_MISC_TILES_WITH_BACKUP.
01:5efe..5f06 function: PLAY_INVALID_SOUND
  rom01:5efe
    notes:
      Besides playing the sound, sets the ff9f flag which is used sometimes to
      check for failed operations.
01:5f07..5f0d function: PLAY_CONFIRM_SOUND
  rom01:5f07
01:5f0e..5f21 function: SET_PALETTES_STANDARD
  rom01:5f0e
    notes:
      Sets all the palettes (ff47..ff49) to standard visible values for the menu.
01:5f22..5f2a function: CLEAR_PALETTES
  rom01:5f22
    notes:
      Clear all the palettes (ff47..ff49) to white.
01:5f2b..5f43 function: CLEAR_GB_WIN_LIGHT_GRAY
  rom01:5f2b
    notes:
      Fills the gb win tilemap (9c00) with solid light gray tiles (0x75).
      (This is the color seen in the background of the start menu.)
01:5f44..5f50 function: MENU_INITIALIZE_GB_WIN
  rom01:5f44
    notes:
      Sets up the internal registers ff40, ff4a, and ff4b for menu mode (full-screen menus like the start menu).
      These use window offsets 0, 7 (full-screen).
      It turns the gb window on and sets the window tilemap address to 9c00.
01:5f51..6021 function: MENU_PARTY_ORDER
  rom01:5f51, rom01:5003, rom00:01a4, rom00:188b
    notes:
      Party order selection menu (pressing "select" button from map).
01:6022..602d function: WAIT_FOR_1_FRAME
  rom01:6022
    notes:
      DMA from cc00.
01:602e..603d function: GET_PARTY_ORDER
  rom01:602e
    inputs:     b <-- player index
    outputs:    a <-- order
01:603e..6057 function: SET_PARTY_ORDER
  rom01:603e
    inputs:     b <-- player index
                a <-- order
01:6058..6062 function: GET_WINDOW_SPRITE_POSITION
  rom01:6058
    inputs:     a <-- selection index (0: ff8c, 1: ff8d)
    outputs:    c <-- x
                b <-- y
01:6063..606d function: SET_WINDOW_SPRITE_POSITION
  rom01:6063
    inputs:     a <-- selection index (0: ff8c, 1: ff8d)
                c <-- x
                b <-- y
01:606e..6079 function: GET_WINDOW_SPRITE_Y_ADDRESS
  rom01:606e
    inputs:     a <-- selection index (0: ff8c, 1: ff8d)
    outputs:   hl <-- address of window sprite y coord (c7a8 + 4 * i)
01:607a..60a2 function: REFRESH_MAGI_LIST
  rom01:607a, rom01:5024
    notes:
      Refreshes the equipped field of the MAGI list (c2da) based on the >player
      equipped MAGI.
01:60a3..60a9 function: GET_MAGI_LIST_BYTE_1 (2)
  rom01:60a3
    notes:
      Duplicated function.
01:60aa..60e1 function: REFRESH_EQUIPPED_MAGI (2)
  rom01:60aa, rom01:5021
    notes:
      Duplicated function.
01:60e2..60e7 function: GET_PLAYER_MAGI_ADDRESS (2)
  rom01:60e2
    notes:
      Duplicated function.
01:60e8..611b function: MENU_PARTY_SELECT
  rom01:60e8, rom01:5006
    notes:
      Select three party members (not the main player character).
01:611c..6156 function: MENU_CHARACTER_SELECT
  rom01:611c
    outputs:   cf <-- set if character selected, unset if canceled
    notes:
      If confirmed, character is loaded automatically based on c709 character index.
      (However, equipment will be cleared even if canceled.)
01:6157..6279 function: MENU_NAME_SELECT
  rom01:6157
    outputs:   cf <-- set if name selected, unset if canceled
01:627a..6289 tile data for solid gray box, used for name entry cursor
01:628a..630a function: MENU_INN
  rom01:628a
01:630b..6331 function: INN_COST_HELPER
  rom01:630b
    inputs:    hl <-- player current hp address
             c745 <-- accumulated gold cost
    outputs:   hl <-- next player current hp address
             c745 <-- accumulated gold cost
    notes:
      Adds to the gold cost (2 bytes) the difference between current and max hp.
      Skipped for characters in Stun/Ston.
01:6332..636b function: RESTORE_PARTY
  rom01:6332, rom01:501e
    notes:
      Restores all party members (including guest if applicable) and MAGI usage.
01:636c..6439 function: RESTORE_SINGLE
  rom01:636c
    inputs:     a <-- player to restore
    notes:
      Restore as RESTORE_PARTY (helper function).
01:643a..6447 function: GET_ITEM_USAGE
  rom01:643a
    inputs:     a <-- item
    outputs:    a <-- usage
    notes:
      Gets the maximum use number for item (e.g. to restore to maximum).
01:6448..6479 function: HEAL_PARTY
  rom1:6448, rom1:5027
    inputs:    bc <-- heal amount
      notes:
        HP heal only.
01:6455..6479 function: HEAL_PLAYER
  rom1:6455
    inputs:     a <-- player
               bc <-- heal amount
    notes:
      HP heal only.
01:647a..664c function: MENU_SHOP
  rom1:647a, rom1:5009
    inputs:     e <-- shop
    notes:
      Shop 0xff is the inn (specially handled by MENU_INN).
01:664d..6656 function: WAIT_FOR_BUTTON
  rom01:664d
    notes:
      Waits until any button is pressed.
      (First, waits for empty input, so press down has to happen after call.)
01:6657..6668 function: GET_EMPTY_INVENTORY_SLOT (alt)
  rom01:6657
    outputs:   cf <-- set if full
               hl <-- address of first empty slot
01:6669..668b function: ITEM_PRICE
  rom01:6669
    inputs:     a <-- item (00..7f or ff)
               de <-- out address (3 bytes)
    outputs:   de <-- out address + 3
      Copies the item price to the out address. Only the first half of items have prices.
      Others are invalid, except 0xff which has special handling to give price of 0xffffff.
01:668c..6710 function: SAVE_MENU
  rom01:668c
    notes:
      Blocking function that executes the selection of a save slot.
      The result is stored in ff8c.
01:6711..674c function: DRAW_SAVE_MENU
  rom01:6711
    inputs:     a <-- save slot index
01:674d..675e function: RESTORE_GAME
  rom01:674d
    notes:
      Restores the saved RAM region at c200 from the backup data at a600.
01:675f..6781 function: LOAD_GAME_AND_CHECK
  rom01:675f
    inputs:     a <-- save slot index
    outputs:   cf <-- if there is a valid save in the given slot
    notes:
      As LOAD_GAME_RAW, but also check the sentinel and checksum to ensure there is a valid save in the given slot.
      If the slot is not valid, CLEAR_GAME is called.
01:6781..678c function: LOAD_GAME_RAW
  rom01:6781
    inputs:     a <-- save slot index
    notes:
      Copies the saved data from the given save slot into RAM at c200.
      This doesn't do any of the other associated processing, though.
01:678d..67bb function: CLEAR_GAME
  rom01:678d
    outputs:
      cf <-- 0 (indicates that there is no valid data in this slot)
    notes:
      Clears out the saved data region (c200..c37f).
      Mostly, this means clearing to 0, but certain regions, like the inventory, are cleared to 0xff.
01:67bc..6806 function: SAVE_GAME
  rom01:67bc
    inputs:     a <-- save slot index
    notes:
      Saves the player's progress an the given save slot.
01:6807..6819 function: SAVE_IO_START
  rom01:6807
    inputs:     a <-- save slot index
    outputs:   hl <-- save slot address
               bc <-- save slot length (0x180)
    notes:
      Calculates the address for the given save slot and SRAM_ENABLE.
01:681a..681f function: STORE_SENTINEL
  rom01:681a
    inputs:    hl <-- address
    notes:
      Stores the two-byte sentinel value 0xe41b at the given address.
      For example, this is approriate to mark valid save data with input hl = a781.
01:6820..6831 function: CALCULATE_SAVE_CHECKSUM
  rom01:6820
    outputs:   hl <-- checksum
    notes:
      Calculates a checksum of the saved RAM area (excluding the saved checksum itself) (c200..c37d).
01:6832..6837 cursor x, y coordinate pairs used by SAVE_MENU
01:6838..699b function: MAIN_MENU
  rom01:6838, rom01:500f
    outputs:   cf <-- 0 is new game, 0 if continue
    notes:
      The main menu that appears when first starting the game ("Start", "Continue").
      This also includes the sound test menu.
      The start path includes the opening text scroll and initial player selection.
01:699c..6a20 function: SOUND_TEST
  rom01:699c
    notes:
      To reach sound test, at the main menu, press B while holding start and select.
01:6a21..6a38 function: CHECK_SRAM
  rom01:6a21
    outputs:    a <-- non-zero if SRAM is valid.
    notes:
      Validates SRAM by testing the global sentinel value at a781.
01:6a39..6a45 function: CLEAR_WINDOW_SPRITES_AND_CURSOR_STOPS_2
  rom01:6a39
    notes:
      As CLEAR_WINDOW_SPRITES_AND_CURSOR_STOPS.
      Additionally, copies the updated data into OAM and calls LOAD_MISC_TILES.
01:6a46..6a50 function: CLEAR_TILEMAP_9800
  rom01:6a46
    notes:
      Clears this tilemap to 0xff.
01:6a51..6a79 function: THE_END
  rom01:6a51, rom01:5036
    notes:
      Displays "The End" animation.
01:6a7a..6a7e function: THE_END_WAIT
  rom01:6a7a
    inputs:     b <-- number of rames
    notes:
      Waits the indicated number of frames. (Returns in vblank.)
01:6a7f..6ac0 function: THE_END_HELPER
  rom01:6a7f
    inputs:     a <-- animation frame index (00..07)
    notes:
      Progresses the unrolling animation of the "The End" banner.
01:6ac1..6ac8 lookup table controlling left side of "The End" animation
01:6ac9..6ad0 lookup table controlling right side of "The End" animation
01:6ad1..6af1 function: INIT_AND_DRAW_STANDARD_BATTLE_WINDOWS
  rom01:6ad1
    notes:
      Draws the monster summary box, player summary box, and guest summary box (if applicable).
      For Arsenal, also draws the cloud background under the menus (which usually
      gets covered up).
01:6ae6..6af1 function: DRAW_STANDARD_BATTLE_WINDOWS
  rom01:6ae6
    notes:
      Different entry point for the above function.
      Note that the player select menu will be ready to execute after returning.
01:6af2..6af8 function: BATTLE_MENU_PLAYER_SELECT
  rom01:6af2
01:6af9..6b51 function: BATTLE_MENU
  rom01:6af9, rom01:5018
01:6b52..6c70 function: BATTLE_ABILITY_MENU
  rom01:6b52
    inputs:  c709 <-- player index
    notes:
      Also includes target selection depending upon the ability chosen.
01:6c71..6ce2 function: MEAT_MENU
  rom01:6c71, rom01:501b, rom00:01dd, rom00:18a0
    notes:
      Includes the meat animation, but no logic after the meat is selected.
01:6ce3..6cee function: RESTORE_BATTLE_WINDOW_BACKUP
  rom01:6c3e
    notes:
      During the meat menu, the battle-text window is backed up to unused VRAM.
      This restores it.
01:6cef..6cf7 function: CHECK_STATUS_CAN_ACT
  rom01:6cef
    inputs:  c709 <-- player index
    outputs:    a <-- 0: okay; nonzero: can't act
    notes:
      Checks the battle-stats to see if the player's status condition prevents taking action.
01:6cf8..6cff function: READ_BATTLE_STAT_1
  rom01:6cf8
    inputs:     a <-- player index
    outputs:    a <-- first battle stat byte (status)
01:6d00..6d11 function: READ_ITEM_1
  rom01:6d00
    inputs:    de <-- item index
    outputs:    a <-- item data
    notes:
      Reads the first byte of item data for the given item
01:6d12..6d3b function: INITIALIZE_BATTLE_WINDOW
  rom01:6d12
    notes:
      Initialize battle window background and clear related data.
01:6d3c..6d5c function: CLEAR_WINDOW_AND_LOAD_ANIMATION_DATA
  rom01:6d3c
    notes:
      Loaded animation data is not specific to actions taken.
      It is simply the standard animation tile data at 4:6000..65ff
01:6d5d..6da8 function: BATTLE_MENU_ENEMY_SELECT
  rom01:6d5d
    notes:
      Relatively specialed menu code due to enemies not being in a menu box.
      Also includes logic about skipping defeated stacks.
01:6da9..6db2 function: IS_ENEMY_STACK_DEFEATED
  rom01:6da9
    inputs:     a <-- enemy index
    outputs:   zf <-- test current stack count
01:6db3..6dbc function: UPDATE_CURSOR_COORDS
  rom01:6db3
    notes:
      Updates the cursor coordinates (ff97, ff98) based on the currently selected
      cursor stop (ff9b).
01:6dbd..6dc4 function: WAIT_FOR_A_B
  rom01:6dbd
    outputs:    a <-- 1: A, 2: B ,3: both
01:6dc5..6df0 function: ARSENAL_CLOUD_PROCESS_UPDATE
  rom01:6dc5, rom01:502d
    notes:
      Expected to be called during vblank, but double checked LY. Modifies tile
      data in-place for the two tiles currently selected by
      ARSENAL_CLOUD_STAGE_UPDATE.
01:6df1..6e26 function: ARSENAL_CLOUD_STAGE_UPDATE
  rom01:6df1, rom01:5033
    notes:
      Counter ffa6 controls the currently selected tiles and rows. The addresses
      of the current tile data are at ffa7 and ffa9. This is the part of the logic
      that doesn't need to touch vram, so it runs from GAME_UPDATE.
01:6e27..6ee6 function: MONSTER_GFX_MORPH
  rom01:6e27, rom01:502a
    inputs:     c <-- new monster gfx id
                b <-- effect flag (0: wave effect, 1: no effect)
    notes:
      Always swaps gfx for the first monster. If the size doesn't match,
      graphics will become corrupted. Wave effect is scaline scroll
      register animation. This is controlled by buffers c800 (offset)
      and c900 (direction).
01:6ee7..6f48 function: DISPLAY_MONSTER_GFX_MORPH_WAVE_EFFECT
  rom01:6ee7
    inputs:  d97d <-- number of frames to display
             ff90 <-- line limit
    notes:
      Displays wave effect on lines up to the line limit (as controlled by
      c800 buffer) for the given number of frames. Copying of actual
      monster gfx data is controlled by d97e..d985 block.
01:6f49..6f73 function: UPDATE_MONSTER_GFX_MORPH_WAVE_EFFECT
  rom01:6f49
    inputs:  ff90 <-- line limit
    notes:
      Updates the c800 and c900 buffers by oscillating the values. Note
      that all entries receive the same processing, so the actual wave
      is only created by ramping up the limit during the start-up of the
      effect.
01:6f74..6fd3 function: DO_MEAT_ANIMATION
  rom01:6f74, rom01:5030
    notes:
      Animation of meat falling from the top of the screen and settling in the center.
      The animated meat is an OAM sprite, but it is copied to the background tilemap at the end.
01:6fd4..6fff unused (?)
0c:4000..401c function: EXECUTE_CSCRIPT
  rom0c:4000
    inputs:    de <-- address of cscript data
0c:401d..402c code to handle cscript expression dispatch
0c:402d..4040 cscript instruction jump table
0c:4041..4083 code to handle cscript instructions 7 and 8: increment and decrement
0c:4084..40b5 code to handle cscript instruction 0: assignment
0c:40b6..40bd code to handle cscript instruction 1: conditional
0c:40be..40cc code to handle cscript instruction 9: indirect cscript call
0c:40cd..40ec code to handle cscript instruction 5: cscript call
0c:40ed..4103 code to handle cscript instruction 6: cscript return
0c:4104..410d code to handle cscript instruction 2: goto
0c:410e..411f code to handle cscript instruction 3: asm call
    notes:
      The call is actually made with a RET statement by pushing the target address. The true
      return address (411c, the last part of this code block) is pushed directly before that.
0c:4120..4128 code to handle cscript instruction 4:
    notes:
      This may be unused. Parameter is assigned to c1a0. ?TODO?
0c:4129..4168 cscript expression jump table
0c:4169..41ea code to handle cscript expression 1f: end expression
0c:41eb..4204 function: CSCRIPT_SKIP
  rom0c:41eb
    inputs:    de <-- current cscript instruction pointer
    outputs:   de <-- current cscript instruction pointer
    notes:
      This computation is somewhat involved because instruction have variable width.
      This is used when failing a cscript conditional test.
0c:4205..4222 code to handle skipping cscript expressions
0c:4223..4238 script skip instruction jump table
0c:4239..424e code to handle skipping cscript expressions
0c:424f..4263 code to handle cscript expression atom (expression 0 is unused, but this code is used)
0c:4264..42b8 code to handle cscript expression 01: multiply
0c:42b9..42cc function: ABS_24
  rom0c:42b9
    inputs:    hl <-- address of high byte of number (offset +2)
    saved:     hl
    notes:
      Computes absolute value of the given 24-bit number in place.
0c:42cd..431f code to handle cscript expression 02: divide
0c:4320..4330 code to handle cscript expression 03: add
0c:4331..4341 code to handle cscript expression 04: subtract
0c:4342..435b code to handle cscript expression 11: subtract (saturated)
0c:435c..4374 code to handle cscript expression 05: bitwise and
0c:4375..438d code to handle cscript expression 06: bitwise or
0c:438e..43a9 code to handle cscript expression 07: bitwise nand
0c:43aa..43c2 code to handle cscript expression 08: bitwise xor
0c:43c3..43db code to handle cscript expression 09: bitwise complement
0c:43dc..43f3 code to handle cscript expression 0a: shift right
0c:43f4..440b code to handle cscript expression 0b: shift left
0c:440c..4475 code to handle cscript expression comparisons
0c:4476..44de function: CSCRIPT_GET_ATOM
  rom0c:4476
    inputs:    de <-- cscript instruction pointer
    notes:
      Parses the cscript instruction stream (advancing the pointer) writing the
      atom to c1a8..c1aa.
0c:44df..467f unused (?)
0d:4000..4151 function: BATTLE
  rom0d:4000
    notes:
      Includes battle setup, (although the encounter must be setup first), battle logic,
      and battle ending.
0d:4152..4163 function: WAIT_FOR_INPUT_AND_END_BATTLE_MODE
  rom0d:4152
    notes:
      Called at the end of battle to wait for an input change then call END_BATTLE_MODE.
0d:4164..4359 more code associated with BATTLE function
0d:435a..4360 function: COUNT_BATTLE_DATA
  rom0d:435a
    inputs:    hl <-- address, typically dx01 for living count
                b <-- number of players/enemies to check
    outputs:    a <-- sum of data at the given address
               hl <-- advanced
0d:4361..437d function: EXECUTE_BATTLE_CSCRIPT
  rom0d:4361
    inputs:     a <-- index of the cscript to run
    notes:
      Address of the cscript is a lookup in the c:4680 table.
0d:437e..43f9 function: COMMIT_BATTLE_DATA
  rom0d:437e
    notes:
      Saves changes from the >battle_data back to >player array. This includes stat changes,
      hp changes, status changes, and inventory changes. Magi usage is also updated.
      Logic from REDUCE_ITEM_USAGE about which items should be deleted is repeated here.
      This includes logic about status effects wearing off after battle.
0d:43fa..4439 function: LOAD_BATTLE_RESISTS
  rom0d:43fa
    inputs:    hl <-- address of battle_data inventory offset (+0x12)
    notes:
      Loops through the characters battle inventory (including magi) and appropriately 
      sets the resistance and weakness battle_data bytes.
0d:443a..4499 function: LOAD_MONSTER_BATTLE_HP
  rom0d:443a
    inputs:    bc <-- address of battle_data for a monster
    notes:
      Loads current HP into battle_stat for each monster in the group.
      This applies the random monster HP variation.
0d:449a..44f3 function: LOAD_PLAYER_BATTLE_DATA
  rom0d:449a
    inputs:    hl <-- >player address
                d <-- upper byte of >battle_data address
    notes:
      Loads most of the >battle_data structure for players, including magi.
0d:44f4..4578 function: LOAD_MONSTER_BATTLE_DATA
  rom0d:44f4
    inputs:     d <-- upper byte of >battle_data address
    notes:
      Loads most of the >battle_data structure for monsters.
0d:4579..45b3 function: CHOOSE_DEFAULT_ABILITIES
  rom0d:4579
    notes:
      Assigns default abilities to each player based on their >status.
      This is determined by an embedded lookup table at 4598..45a7.
      "Slep" gets 0x010f and "Para" gets 0x010e. Otherwise 0x00ff ("No Move").
0d:45b4..4fff unused
0d:5000..501a various entry points for functions in the 5000..63ff region
0d:501b..502b function: BATTLE_ANIMATION
  rom0d:501b, rom0d:5000
    inputs:  d910 <-- animation id
    notes:
      High bit set indicates a "procedural" animation versus data-driven.
0d:502c..50e6 function: BATTLE_ANIMATION_HELPER
  rom0d:502c
    inputs:  d910 <-- animation id
    notes:
      Implements the logic for BATTLE_ANIMATION.
      Calls part of itself at 50a6 when displaying the animation separately on
      multiple groups (group controlled by d974).
0d:50e7..51af function: DECODE_ANIMATION_DATA
  rom0d:50e7
    inputs:     a <-- control byte (00..7f)
    notes:
      In the given control byte (a):
        bit 0..3: frame length
        bit 4     palette flag
        bit 5     x flip flag
        bit 6     y flip flag
      Reads an additional byte from the d93d stream as an index into animation
      frame data (1:7000). This data is decoded into cc00. >battle_anim_tile_info
0d:51b0..51d5 function: ANIMATION_FRAME_Y_FLIP
  rom0d:51b0
    notes:
      Y flip the animation frame at d943..4972.
0d:51d6..51fe function: ANIMATION_FRAME_X_FLIP
  rom0d:51d6
    notes:
      X flip the animation frame at d943..4972.
0d:51ff..520f function: ANIMATION_STREAM_READ
  rom0d:51ff
    outputs:    a <-- byte
    notes:
      Reads a byte from the animation stream at d93d (advancing the stream).
0d:5205..520f function: ANIMATION_STREAM_SET_POSITION
  rom0d:5205
    inputs:    hl <-- address
    notes:
      Sets the position of the d93d stream.
0d:5210..5218 function: ANIMATION_STREAM_GET_POSITION
  rom0d:5210
    outputs:   hl <-- address
    notes:
      Gets the position of the d93d stream.
0d:5219..5226 function: ANIMATION_FRAME_DEREFERENCE
  rom0d:5219
    inputs:    hl <-- address in animation frame index region 1:7000
    outputs:   hl <-- address of data
0d:5227..5234 function: ANIMATION_DEREFERENCE
  rom0d:5227
    inputs:    hl <-- address in the animation stream index region 7:6800
    outputs:   hl <-- address of stream
0d:5235..523f function: ANIMATION_STREAM_READ_BYTE
  rom0d:5235
    inputs:    hl <-- animation stream address
    outputs:    a <-- byte
               hl <-- hl + 1
    notes:
      Really just reads a byte from bank 7.
0d:5239..523f function: ANIMATION_FRAME_READ_BYTE
  rom0d:5239
    inputs:    hl <-- animation frame data address
    outputs:    a <-- byte
               hl <-- hl + 1
    notes:
      Really just reads a byte form bank 1.
0d:523b..523f function: STREAM_READ_HELPER
  rom0d:523b
    inputs:    hl <-- address
                a <-- bank
    outputs:    a <-- byte
               hl <-- hl + 1
0d:5240..5250 function: ANIMATION_ADJUST_X
  rom0d:5240
    notes:
      Adjusts the x position (d940) based on the current enemy (d974)'s x position (d933..d935).
0d:5251..525a function: IS_ENEMY_STACK_DEFEATED
  rom0d:5251
    inputs:     a <-- enemy index
    outputs:   zf <-- test current stack count
    notes:
      Same as the function in bank 1.
0d:525b..52df function: LOAD_MONSTER_GFX_METADATA
  rom0d:525b, rom0d:5012
    notes:
      Based on enemies loaded into battle_data (d500), populate:
        d921: graphics bank and index
        d927: dimensions
        d933: x offsets (for animation)
        d936: x offsets (for drawing)
        d939: y offsets (for drawing)
0d:52e0..5301 function: LOAD_MONSTER_GFX_DIMENSIONS
  rom0d:52e0, rom0d:5015
    notes:
      Based on enemy graphics data loaded at d921, populate dimensions at d927.
0d:5302..533d function: LOAD_MONSTER_GFX_ADDRESS
  rom0d:5302, rom0d:5018
    inputs:  ff90 <-- enemy index
    outputs:   hl <-- monster gfx address
                a <-- monster gfx bank
               bc <-- monster gfx size
0d:533e..534f function: PROCEDURAL_BATTLE_ANIMATION
  rom0d:533e
    inputs:     a <-- animation id
0d:5350..535a function: PROCEDURAL_BATTLE_ANIMATION_HELPER
  rom0d:5350
    inputs:     a <-- animation id
    notes:
      Lookup from jump table d:5eb5. (Individual procedural battle animations indicated
      by [PBAxx].)
0d:535b..5377 function: [PBA00] PBA_WINDOW_SHAKE_VERTICAL
  rom0d:535b
    notes:
      Vertical shaking effect on the window. Uses lookup table at d:5f03.
0d:5378..5398 function: [PBA01] PBA_FADE_OUT
  rom0d:5378
    notes:
      Fade out monster graphics. Palette animation during animation, but then graphics tiles
      are cleared so that subsequent operations don't need to worry about palette animation
      (as palettes will also affect window if note used carefully). Fade out flag at d97a
      will be set. (If still faded out at end of character action, it will fade back in
      automatically.)
0d:5399..53cd function: PBA_FADE_IN
  rom0d:5399, rom0d:5003
    notes:
      Not mapped to a PBA number, but will be called at the end of the character action if
      the screen is still faded out (d97a).
0d:53ce..53d2 function: ERASE_MONSTER_GFX
  rom0d:53ce
    notes:
      Clears graphics at 9800..98ff which is the part of the background with monster graphics.
      Used during PBA_FADE_OUT. (The grahpics will stay erased until fade-in.)
0d:53d3..53dc function: ERASE_WINDOW_MONSTER_GFX
  rom0d:53d3
    notes:
      Clears graphics at 9c00:9cff. This is in preparation for displaying the window over the
      entire screen at WY = 0 where this part of the graphics will map to where the monsters
      usually are.
0d:53dd..53ee function: COPY_WINDOW_TO_BACKGROUND
  rom0d:53dd
    notes:
      Copies the normally visible part of the battle window to the corresponding address of the
      main background, then turns the window off. (This allows the window to be used for another
      purpose without changing what is currently visible.)
0d:53ef..53fc function: USE_FULL_BATTLE_WINDOW
  rom0d:53ef
    notes:
      Enables window at WY = 0, covering the entire background. The data in the window would be
      a duplicate of the background (including the original window data at appropriate offset),
      but monster gfx might be modified. This will allow monster gfx in the actual background to
      be changed without becoming visible until the window is restored to normal operation.
      Old WY is backed up with d97b.
0d:53fd..5426 function: RESTORE_NORMAL_BATTLE_WINDOW
  rom0d:53fd
    notes:
      Restores the normal contents of the battle window from the data (expected to have been) previously
      copied to the normal background at 9900. Data under the normally visible part the window is cleared
      out (perhaps to avoid garbage when shaking), and the background data underneath the window is
      cleared out (perhaps for the same reason). WY is restored from d97b and the window is enabled.
0d:5427..5445 function: [PBA02] PBA_FADE_OUT_IN
  rom0d:5427
    notes:
      Fade out effect followed immediately by fade in. Note, this clears d97a but doesn't touch
      monster graphics actually loaded into the background, so using this after PBA_FADE_OUT would
      probably prevent monster graphics from reloading.
0d:542a..5445 function: MONSTER_GFX_FADE_IN
  rom0d:542a
    notes:
      Palette animation to fade out monster graphics. Palette changes are transient; monster graphics
      are distinguished by LY 0x40. Returns at LY 0x40. Fade palettes at d:5f0f.
0d:5446..5462 function: MONSTER_GFX_FADE_OUT
  rom0d:5446
    notes:
      As MONSTER_GFX_FADE_IN, but different palettes. Fade palettes at d:5f0b.
0d:5463..5479 function: DISPLAY_MONSTER_GFX_WITH_PALETTE
  rom0d:5463
    inputs:     e <-- palette
    notes:
      Uses palette for lines 0..0x39 (where monster gfx are displayed). (This replaces all three
      palette: bg0, bg1, obj.) Normal palette d:5f0e is restored at LY 0x40 where this returns.
0d:547a..54c1 function: [PBA12] PBA_MONSTER_SHAKE_HORIZONTAL
  rom0d:547a
0d:54c2..54dc function: [PBA03] PBA_MONSTER_SHAKE_SEVERE
  rom0d:54c2
    notes:
      Alternating horizontal and vertical shakes driven by data at d:5f03
0d:54dd..54e7 function: SHAKE_HELPER
  rom0d:54dd
    inputs:    hl <-- shake data address
                b <-- shake length
                c <-- shake register (0x43 or 0x42 to target ff43 or ff42)
0d:54e8..5511 function: [PBA04] PBA_FLICKER
  rom0d:54e8
    notes:
      Double full-screen flicker by turning off LCD. (Some emulators may not handle this well.)
0d:5512..5512 function: [PBA05] PBA_NOP (1)
  rom0d:5512
    notes:
      Does nothing.
0d:5513..55ae function: [PBA06] PBA_LIFT
  rom0d:5513
    notes:
      Moves the enemy up off the screen. The enemy won't descend until the end of the current
      turn (or won't descend at all if it dies). The d986 "lift" flag is set on the enemy
      so that the end of turn handling can bring the enemy back down. Supports cff5 of 9
      to lift each enemy one after the other (by calling into itself at 5533).
      (Animation is tile-by-tile, not smooth.)
0d:55af..55bb function: GET_MONSTER_GFX_DIMENSIONS
  rom0d:55af
    inputs:  ff90 <-- enemy index
    outputs:    b <-- height
                c <-- width
    notes:
      Loads the requested data from d927.
0d:55bc..55c2 function: STORE_LIFT_WRITE_POSITION
  rom0d:55bc
    inputs:    hl <-- lift write position
    notes:
      Stores hl into d975..d976. This is the next VRAM address where the monster will be drawn.
0d:55c3..55cb function: LOAD_LIFT_WRITE_POSITION
  rom0d:55c3
    outputs:   hl <-- lift write position
    notes:
      Loads the data previously stored by STORE_LIFT_WRITE_POSITION.
0d:55cc..55d2 function: LOAD_LIFT_CLEAR_POSITION
  rom0d:55cc
    outputs:   hl <-- lift clear position
    notes:
      Loads the data previously stored by STORE_LIFT_CLEAR_POSITION.
0d:55d3..55db function: STORE_LIFT_CLEAR_POSITION
  rom0d:55d3
    inputs:    hl <-- lift clear position
    notes:
      Stores hl into d977..d978. This is the next VRAM address where a previously drawn monster
      row will be cleared.
0d:55dc..5610 function: [PBA07] PBA_SCROLL
  rom0d:55dc
    notes:
      "Scrolls" the enemy by repeatedly moving the top tile row to the bottom (and moving the
      others up). Supports cff5 of 9 to lift each enemy one after the other (by calling into
      itself at 55f8).
0d:5611..561a function: IS_ENEMY_STACK_DEFEATED_2
  rom0d:5251
    inputs:  ff90 <-- enemy index
    outputs:   zf <-- test current stack count
    notes:
      Same as the similarly named function except for calling convention. (Register "a" is not
      preserved.)
0d:561b..563b function: SCROLL_HELPER
  rom0d:561b
    inputs:  ff90 <-- enemy index
    notes:
      Perhaps the PBA_SCROLL "scroll" operation in buffered enemy graphics data at
      c800 + 0x100 * (enemy index). Note that an additional row of the buffer is used.
0d:563c..5654 function: COPY_BUFFERED_MONSTER_GFX
  rom0d:563c
    inputs:  ff90 <-- enemy index
    notes:
      Copies the monster graphics data buffered at c800 + 0x100 * (enemy index) back to
      VRAM.
0d:5655..566a function: BUFFER_MONSTER_GFX
  rom0d:5655
    inputs:  ff90 <-- enemy index
    notes:
      Copies the monster graphics data from VRAM to buffer at c800 + 0x100 * (enemy index).
0d:566b..567b function: SETUP_MONSTER_GFX_BUFFER_DATA
  rom0d:566b
    inputs:  ff90 <-- enemy index
    outputs:    b <-- width
                c <-- height
               hl <-- VRAM address
               de <-- buffer address
    notes:
      Loads the requested data for the given enemy's graphics. The buffer will be at
      c800 + 0x100 * (enemy index) (as used by the preceeding subroutines).
0d:567c..5686 function: GET_MONSTER_GFX_DIMENSIONS_2
  rom0d:567c
    inputs:  ff90 <-- enemy index
    outputs:    b <-- height
                c <-- width
                a <-- enemy index * 2
    notes:
      Loads the requested data from d927. Same as the similarly named function, but doesn't
      save hl.
0d:5687..569b function: [PBA08] PBA_KILL
  rom0d:5687
    notes:
      Instantly kill the enemy group by setting the stack count to 0. This must only be used
      by an attack that expects the animation to do this processing. If cff5 is 9, nothing
      happens.
0d:569c..56bf function: [PBA09] PBA_LINE
  rom0d:569c
    notes:
      Displays a white horizontal line through the middle of the enemies
      (accomplished via scanline palette animation).
0d:56c0..56c0 function: [PBA0a] PBA_NOP (2)
  rom0d:56c0
    notes:
      Does nothing.
0d:56c1..56fa function: [PBA0b] PBA_RAIN
  rom0d:56c1
    notes:
      Rain effect using particle system with update function 0x06 and global
      update function 0x07. c8a0 is used as lifetime counter ticking up,
      but only meaningful by activating at 0 (so starting at ff). c995 is
      respawn counter.
0d:56fb..5722 function: [update06]
  rom0d:56fb
    inputs:  ff90 <-- particle index
    notes:
      Particle update that moves the particle down. If the particle has
      moved off screen, decrement the respawn counter c995 to setup the
      particle to spawn again, or if the counter is exhausted, kill the
      particle.
0d:5723..575f function: [update07]
  rom0d:5723
    notes:
      Global particle update. Ticks up lifetime counter c8a0 and at 0
      sets y coordinate of zero and random x coordinate.
0d:5760..57b4 function: [PBA0c] PBA_CURSE_SONG
  rom0d:5760
    notes:
      Two particle effects that run sequentially. First is PBA_SONG. Second
      is the "wraith" effect that appears over each enemy. This is a simple
      particle effect using update function 0x09 and global update function
      0x0a.
0d:57b5..57c5 function: [update09]
  rom0d:57b5
  inputs:  ff90 <-- particle index
    notes:
      Particle update that accelerates the particle up, then kills it once
      it reaches a certain velocity threshold.
0d:57c6..57ca function: [update0a]
  rom0d:57c6
    notes:
      Global particle update that simple renders frames.
0d:57cb..5800 function: SETUP_ENEMY_PARTICLES
  rom0d:57cb
    outputs: ff91 <-- number of particles
    notes:
      Setup particle positions for the first three particles based on
      enemy graphics positions.
0d:5801..5851 function: [PBA11] PBA_HEART_SONG
  rom0d:5801
    notes:
      Two particle effects that run sequentially. First is PBA_SONG. Second
      is the heart effect. This uses update function 0x01 and global update
      function 0x02.
0d:5852..58a9 function: [update01]
  rom0d:5852
    inputs:  ff90 <-- particle index
    notes:
      Particle update that moves the particle up, then kills it once
      it reaches the top of the screen. The particle has alternating left
      and right accleration controlled by c8a0.
0d:58aa..58cd function: [update02]
  rom0d:58aa
    notes:
      Switches OAM for each particle between 3 and 4 (used for heart particles)
      on certain frames controlled by ff91 (which is expected to be initialized
      but not modified by any other concurrent code).
0d:58ce..5918 function: [PBA21] PBA_SONG
  rom0d:58ce
    notes:
      Music notes particle effect. Uses update function 0x03 and global update
      function 0x04.
0d:5919..5960 function: [update03]
  rom0d:5919
    inputs:  ff90 <-- particle index
    notes:
      Particle update that moves the particle up, with alternating left and
      right acceleration. Direction is controlled by c8a0.
0d:5961..5974 function: [update04]
  rom0d:5961
    notes:
      Tracks time in c995, then kills all particles when time expires.
0d:5975..59ad function: [PBA0d] PBA_QUESTION
  rom0d:5975
    notes:
      Particle effect showing random question marks at the top of the screen.
      Update function is 0x00 (nop) and global update function 0x0b. c8a0 is
      used as a lifetime counter, and c995 is a respawn counter.
0d:59ae..5a0f function: [update0b]
  rom0d:59ae
    notes:
      Particle global update. Spawned particles tick up lifetime counter
      c995. At 0, position is set to random x position and y=0, then
      on the next update it will be despawned again. This ticks the respawn
      counter and the particle will be killed when the respawn counter is
      exhausted.
0d:5a10..5a19 function: [PBA13] PBA_SHORT_EXPLOSIONS
  rom0d:5a10
    notes:
      Random explosions. Shares most code with PBA_LONG_EXPLOSIONS (ie Flare)
      with animation rate controlled by ff92 and respawn count controlled by
      ff90.
0d:5a1a..5a51 function: [PBA0e] PBA_LONG_EXPLOSIONS
  rom0d:5a1a
    notes:
      Random explosions. Particle effect using particle update function 00
      (nop) and global update function 5. ff92 controls render length of the
      global update function. c995 is respawn count and c8a0 is lifetime
      counter (counting up) also controlling OAM via update function.
0d:5a52..5ac7 function: [update05]
  rom0d:5a52
    notes:
      Increments life counter (c8a0) for each particle. At 0, initialized
      random x and y position. Controls OAM (c8c8) based on counter, then
      stops at 3 and respawns (based on c995) or kills.
0d:5ac8..5acd function: [PBA0f] PBA_GATHER_AND_FADE_OUT
  rom0d:5ac8
    notes:
      PBA_GATHER followed by PBA_FADE_OUT.
0d:5ace..5afa function: PBA_GATHER
  rom0d:5ace
    notes:
      Particle effect of random dots moving toward the center of the screen.
      c8a0 is a lifetime counter, c8f0 is angle, and c918 is radius. Uses
      update function 00 (nop) and global update function 0e.
0d:5afb..5b85 function: [update0e]
  rom0d:5afb
    notes:
      c8a0 counter spawns at 0 and then is used to control OAM setting.
      Particles are spawned with a random angle (c8f0) and radius (c918).
      Angle is measured with 0x100 mapping to 360 degrees (so 1 byte covers
      the full circle). Each update, x and y are caculated from the polar
      coordinates, then radius is decremented. When radius falls below 0,
      the particle is killed.
0d:5b86..5bbe function: PBA_CIRCLES
  rom0d:5b86
    notes:
      Particle effect of random circles appearing on the screen. c8a0
      is a lifetime counter and c995 is a respawn counter. Uses update
      function 00 (nop) and global update function 08.
0d:5bbf..5c36 function: [update08]
  rom0d:5bbf
    notes:
      Advances lifetime counter c8a0. Spawn at 0 with random x and y and
      controls OAM setting. Despawns at 2 and may respawn or die based on
      respawn counter c995.
0d:5c37..5c37 ? (just a return statement)
0d:5c38..5c4d function: [PBA15] PBA_ARSENAL_CANNON_1
  rom0d:5c38
    notes:
      Animation for Arsenal's cannon attack. There is a separate function
      for each of the four cannons (though they share code).
0d:5c4e..5c64 function: [PBA16] PBA_ARSENAL_CANNON_2
  rom0d:5c4e
0d:5c65..5c7b function: [PBA17] PBA_ARSENAL_CANNON_3
  rom0d:5c65
0d:5c7c..5c90 function: [PBA18] PBA_ARSENAL_CANNON_4
  rom0d:5c7c
    notes:
      Falls through to ARSENAL_CANNON_BEAM.
0d:5c91..5cf8 function: ARSENAL_CANNON_BEAM
  rom0d:5c91
    inputs:     c <-- x
                b <-- y
             ff90 <-- OAM type (first frame)
             ff91 <-- OAM type (diagonal frames)
             ff92 <-- motion type: 0: +x +y 1: -x -y 2: +x -y 3: -x +y
    notes:
      Displays the beam part of Arsenal's cannon animation.
0d:5cf9..5cfd function: RENDER_CC00_FRAME
  rom0d:5cf9
    notes:
      Renders a frame using staged OAM from cc00.
0d:5cfe..5d0d function: LOAD_EXPLOSION_SPRITES
  rom0d:5cfe
    notes:
      Loads explosion sprites used by PBA_SHORT_EXPLOSIONS,
      PBA_LONG_EXPLOSIONS, and the Aresenal cannon animation.
      These are loaded into VRAM starting at 8000. The data
      is 4:6330..63bf and 4:6440..649f.
0d:5d0e..5d3b function: ARSENAL_CANNON_EXPLOSION
  rom0d:5d0e
    inputs:     c <-- x
                b <-- y
    notes:
      Displays the explosion part of Arsenal's cannon animation.
      (This precedes the beam.)
0d:5d3c..5dd2 function: [PBA19] PBA_LAUNCH_SMASHER
  rom0d:5d3c
    notes:
      Three parts: changing Arsenal's graphics to show the hatch opening;
      flickering startup animation shown over the hatch; and particle
      animation of the four fireballs. Particle animation uses update
      function 0x0c and global update 0x0d. Note that the hatch doesn't
      close until after the turn is over (handled elsewhere).
0d:5d87..5dd2 function: [PBA1c] PBA_LAUNCH_SMASHER_FIREBALLS
  rom0d:5d87
    notes:
      Alternate entry point for PBA_LAUNCH_SMASHER that only show the
      fireball particles (and plays the accompanying sound).
0d:5dd3..5e07 function: [update0c]
  rom0d:5dd3
    inputs:  ff90 <-- particle index
             ff91 <-- radius
    notes:
      c8a0 is angle (scaled so 0x100 is 360 degrees). Sets x and y position
      based on angle and radius, and advances angle. Oam type alternates.
0d:5e08..5e1f function: [update0d]
  rom0d:5e08
    notes:
      Updates ff91 (used as radius for particles), then kills all (four)
      particles when the correct threshold is reached.
0d:5e20..5e37 function: LOAD_ARSENAL_HATCH_TILES
  rom0d:5e20
    inputs:    hl <-- tile data address
    notes:
      Loads tiles for one of the animation frames of Arsenal's hatch
      (from the given address in bank 4).
0d:5e38..5e40 function: BACKUP_ARSENAL_HATCH_TILES
  rom0d:5e38
    inputs:    hl <-- vram tile data address
               de <-- backup address
    outputs:   hl <-- next vram tile data address
               de <-- next backup address
    notes:
      Call three times to backup all parts of the data.
0d:5e41..5e47 function: LOAD_ARSENAL_HATCH_TILES_HELPER
  rom0d:5e41
    inputs:    hl <-- tile data address
               de <-- vram tile data address
    outputs:   hl <-- next tile data address
0d:5e48..5e52 function: INITIALIZE_LAUNCH_SMASHER_START
  rom0d:5e48
    notes:
      Stages OAM data for flickering animation that displays after
      Arsenal's hatch opens for launching smasher, but before fireballs
      appear.
0d:5e53..5e6d function: [PBA1a] PBA_CLOSE_HATCH
  rom0d:5e53
    notes:
      Animation to close Arsenal's hatch after smasher has been launched.
      If used with another enemy, graphics may be come corrupted (or if
      the modified graphics are not used, there may be no visible effect
      besides the delay). May also cause corruption is the hatch hasn't
      opened as d98c buffer may not be filled correctly.
0d:5e6e..5e72 function: [PBA1b] [PBA1e] PBA_MORPH_APOLLO
  rom0d:5e6e
0d:5e73..5e77 function: [PBA1f] PBA_MORPH_APOLLO_INJURED
  rom0d:5e73
0d:5e78..5e81 function: [PBA20] PBA_MORPH_APOLLO_INJURED_2
  rom0d:5e78
    notes:
      Part of this code is shared by all the PBA_MORPH_* functions.
0d:5e82..5e88 function: [PBA22] PBA_MEAT
  rom0d:5e82
    notes:
      Meat animation. (This is not suitable for use as an actual
      battle animation.)
0d:5e89..5e95 function: PARTICLE_RANDOM_START_DELAY
  rom0d:5e89
    inputs:     d <-- minimum (inclusive)
                e <-- maximum (inclusive)
                b <-- count
0d:5e96..5eb3 particle update function jump table
0d:5eb4..5eb4 function: [PBA1d] [update00] PBA_NOP (3)
  rom0d:5eb4
    notes:
      This is actually useful at least as a particle update function.
0d:5eb5..5efa procedural battle animation (PBA) jump table
0d:5efb..5f02 address table for smasher hatch close animation
0d:5f03..5f0a shake offset lookup table
0d:5f0b..5f0d fade out palette lookup table
0d:5f0e..5f0e standard palette (used by fading effects)
0d:5f0f..5f11 fade in palette lookup table
0d:5f12..5f12 unused (?) (maybe unneeded part of previous table)
0d:5f13..5f1c heart particle effect start delay lookup table
0d:5f1d..5f26 heart particle effect start x position lookup table
0d:5f27..5f30 song particle effect start delay lookup table
0d:5f31..5f3a unused (???) ?TODO?
0d:5f3b..5f4e song particle effect start position lookup table
0d:5f4f..5f60 ?TODO?
0d:5f61..5f77 function: POLAR_OFFSET
  rom0d:5f61
    inputs:     c <-- x
                b <-- y
                d <-- r
                e <-- theta (0x100 is 360 degrees)
    outputs:    c <-- x + r*cos(theta)
                d <-- y + r*sin(theta)
0d:5f78..5f9a function: SINE
  rom0d:5f78
    inputs:     a <-- theta
                d <-- r
    outputs:    a <-- r*sin(theta)
0d:5f9b..5fbb function: DIVIDE_U16_U8
  rom0d:5f9b
    inputs:    hl <-- dividend
                a <-- divisor
    outputs:   hl <-- quotient
                a <-- remainder
0d:5fbc..6027 function: EXECUTE_PARTICLES
  rom0d:5fbc
    notes:
      Run particles to completion based on data in the c800 particle buffer.
      (Refer to the particle section under "Other".)
0d:6028..6030 function: PARTICLE_UPDATE
  rom0d:6028
    inputs:     a <-- update function index
    notes:
      Execute a particle update function (uses jump table at d:5e96). This
      can also be a global particle update function. ff90 may be index for
      particle update. ff92 is sometimes a parameter for global update.
0d:6031..603d function: PARTICLE_INITIALIZE_WITH_START_DELAY
  rom0d:6031
    inputs:    hl <-- initial start delay array
    notes:
      As PARTICLE_INITIALIZE, but initialize the first 0x0a elements of the
      start delay array (c800) with data from the given address.
0d:603e..604d function: PARTICLE_INITIALIZE
  rom0d:603d
    notes:
      Initializes the particle buffer to zero, or 0x80 for velocities (which
      counts as zero velocity). Touches c800..c98f only (no "control" bytes).
0d:604e..6056 function: KILL_PARTICLE
  rom0d:604e
    inputs:  ff90 <-- particle index
0d:6057..6067 function: ARE_ALL_PARTICLES_DEAD
  rom0d:6057
    outputs:   zf <-- set when all particles are dead
0d:6068..6078 function: STAGE_PBA_OAM_DATA
  rom0d:6068
    inputs:     c <-- x
                b <-- y
                a <-- oam type
    notes:
      The oam staging address is read from c990..c991, and this pointer is
      advanced to the next block. As such, this is mostly to be used by the
      particle system, but other things can also use it.
0d:606b..6078 function: STAGE_PBA_OAM_DATA_AT_ADDRESS
  rom0d:6068
    inputs:     c <-- x
                b <-- y
                a <-- oam type
               hl <-- address
    notes:
      As STAGE_PBA_OAM_DATA, but the address is passed in hl. The address
      of the next block is stilled saved back to c990..c991.
0d:6079..6081 function: GET_PBA_OAM_STAGING_ADDRESS
  rom0d:6079
    outputs:   hl <-- address
    notes:
      Reads the oam staging address from c990..c991.
0d:6082..60b8 function: STAGE_PBA_OAM_DATA_HELPER
  rom0d:6082, rom0d:500f
    inputs:     c <-- x
                b <-- y
                a <-- oam type
               hl <-- address
    outputs:   hl <-- address of next block
    notes:
      Implementation for the above two functions. This can also be called
      directly to avoid dealing with c990..c991 altogether.
0d:60b9..60bd function: READ_PBA_OAM_TYPE_DATA
  rom0d:60b9, rom0d:500c
    inputs:    hl <-- address
    outputs:    a <-- data
               hl <-- address + 1
    notes:
      Just reads the address from bank f, but meant for reading oam type data.
0d:60be..60c5 function: LOAD_ANIMATION_GFX
  rom0d:60be, rom0d:5009
    inputs:    hl <-- address (bank 4)
                b <-- size
    notes:
      Copies the requested data from bank 4 to VRAM at 8000.
0d:60c1..60c5 function: LOAD_ANIMATION_GFX_AT_ADDRESS
  rom0d:60be, rom0d:5009
    inputs:    hl <-- source address (bank 4)
                b <-- size
               de <-- destination address (vram)
0d:60c6..60cd function: CLEAR_STAGED_OAM_CC
  rom0d:60c6
    notes:
      Clears staged oam data at cc00.
0d:60ce..60da function: RENDER_CC00_FRAME_WITH_NO_OAM
  rom0d:60ce
    notes:
      Clears staged oam data at cc00, then renders a frame using the now-empty
      data.
0d:60d3..60da function: RENDER_CC00_FRAMES
  rom0d:60de, rom0d:5006
    inputs:     b <-- number of frames to render
    notes:
      Renders frames using staged OAM from cc00.
0d:60db..63ff unused (?)
0e:4000..4005 various entry points for functions in the 4000..49f4 region
0e:4006..4047 function: UPDATE_AUDIO
  rom0e:4006, rom0e:4000
    inputs:  ffb0 <-- background music id
             ffb1 <-- foreground music id
             ffb2 <-- sound id
             ffb9 <-- (write 0 to stop foreground music)
    notes:
      Should be called exactly once a frame to update the currently playing
      music track and currently playing sound (plus check for changes based
      on the inputs).
0e:4048..4080 function: RESET_AUDIO
  rom0e:4048, rom0e:4003
    notes:
      Stops all audio and resets all audio system memory to standard initial
      state.
0e:4081..409a function: RESET_MUSIC_CONTROL
  rom0e:4081
    notes:
      Resets the music control block cb00..cb49 to initial state. (Doesn't
      touch hardware audio system.)
0e:409b..40cd function: PLAY_BACKGROUND_MUSIC
  rom0e:409b
    inputs:     a <-- music id
    notes:
      Starts playing the given music track from the beginning in background
      music mode. (If input is 0, music will be turned off.)
0e:40a4..40cd function: PLAY_MUSIC
  rom0e:40a4
    inputs:     a <-- music id
    notes:
      Starts playing the given music track from the beginning. Input is
      expected to be non-zero.
0e:40ce..40ea function: SILENCE_AUDIO
  rom0e:40ce
    notes:
      Writes to the hardware data to silence all four hardware audio channels.
0e:40eb..410c function: PLAY_FOREGROUND_MUSIC
  rom0e:40eb
    inputs:  ffb1 <-- music id
    notes:
      Starts playing the given music track from the beginning in foreground
      music mode. Any previously playing music state is backed up in cb62.
0e:410d..416a function: UPDATE_FOREGROUND_MUSIC
  rom0e:410d
    notes:
      Called each frame when foreground music is active. When the music track
      has ended, restore the previously saved background music track.
0e:411d..416a function: STOP_FOREGROUND_MUSIC
  rom0e:411d
    notes:
      Stops foreground music and restores the previously saved background
      music track.
0e:414a..416a function: STOP_SOUND_TONE_CHANNEL
  rom0e:414a
    notes:
      Restores the sound tone channel to its state as tracked by the music
      system.
0e:416b..4174 function: STOP_SOUND_NOISE_CHANNEL
  rom0e:416b
    notes:
      Silences the noise channel. (The music system doesn't use this channel.)
0e:4175..418c default state for >music_control (same for each channel) (overlap!)
0e:418c..4194 default pitch effect
0e:4195..4199 default volume effect
0e:419a..4243 note frequency lookup table
0e:4244..4250 note length lookup table
0e:4251..4280 function: UPDATE_MUSIC
  rom0e:4251
    notes:
      Should be called every frame to process the currently playing music track.
0e:4281..47ab function: UPDATE_MUSIC_STREAMS
  rom0e:4281
    notes:
      Should be called at a frequency depending on the current music tempo.
      (The game calls this twice per "beat", perhaps to allow for faster
      tempos.) Much of this code block is made up of the command byte
      handler functions and other helpers. (This is broken down below.)
0e:4281..4350 top level processing for game music channel 0 (hw channel 2)
0e:4351..4356 function: DISPATCH_MUSIC_CHANNEL_0_COMMAND
  rom0e:4351
    inputs:    hl <-- indexed jump table address
    notes:
      Jumps to the address stored at the input address.
0e:4357..4357 function: MUSIC_COMMAND_NULL_HANDLER
  rom0e:4357
    notes:
      Does nothing (unhandled/unsupported music command).
0e:4358..436f jump table for music channel 0 commands [ch0.x]
0e:4370..4390 function: [ch0.0] MUSIC_CHANNEL_0_VOLUME_EFFECT
  rom0e:4370
    notes:
      Reads two byte address from channel 0 stream into the volume
      control addresses.
0e:4391..43a0 function: [ch0.1] MUSIC_CHANNEL_0_JUMP
  rom0e:4391
    notes:
      Reads two byte address from channel 0 stream and jumps stream
      to that address.
0e:43a1..43b0 function: [ch0.2] MUSIC_CHANNEL_0_LOOP_COUNTER_JUMP
  rom0e:43a1
    notes:
      Decrements the loop counter and jump unless the loop counter has
      reached zero.
0e:43b1..43c0 function: [ch1.2] MUSIC_CHANNEL_1_LOOP_COUNTER_JUMP
  rom0e:43b1
0e:43c1..43d0 function: [ch2.2] MUSIC_CHANNEL_2_LOOP_COUNTER_JUMP
  rom0e:43c1
0e:43d1..43e0 function: [ch0.9] MUSIC_CHANNEL_0_LOOP_COUNTER_2_JUMP
  rom0e:43d1
    notes:
      As MUSIC_CHANNEL_0_LOOP_COUNTER_JUMP, but using the alt loop
      counter (cb19).
0e:43e1..43f0 function: [ch1.9] MUSIC_CHANNEL_1_LOOP_COUNTER_2_JUMP
  rom0e:43e1
0e:43f1..4400 function: [ch2.9] MUSIC_CHANNEL_2_LOOP_COUNTER_2_JUMP
  rom0e:43f1
0e:4401..4409 function: MUSIC_STREAM_READ_2_HELPER
  rom0e:4401
    inputs:    hl <-- address of music stream pointer (e.g. cb04)
    outputs:    c <-- first byte
                a <-- second byte
               de <-- advanced stream pointer
               hl <-- += 1
0e:440a..4413 function: MUSIC_STREAM_CONDITIONAL_JUMP
  rom0e:440a
    inputs:    zf <-- result of loop counter decrement
               hl <-- address of music stream pointer + 1 (e.g. cb05)
               de <-- advanced stream pointer (result if zf)
               bc <-- jump address (result if not zf)
0e:4414..4434 function: [ch0.b] MUSIC_CHANNEL_0_COMPARE_JUMP
  rom0e:4414
    notes:
      Reads a byte from the stream (plus 2 byte jump address) and jump
      if the loop counter is equal to that byte.
0e:4435..4455 function: [ch1.b] MUSIC_CHANNEL_1_COMPARE_JUMP
  rom0e:4435
0e:4456..4476 function: [ch2.b] MUSIC_CHANNEL_2_COMPARE_JUMP
  rom0e:4456
0e:4477..447d function: [ch0.3] MUSIC_CHANNEL_0_LOOP_COUNTER
  rom0e:4477
    notes:
      Reads a byte from the stream into the loop counter.
0e:447e..4484 function: [ch0.a] MUSIC_CHANNEL_0_LOOP_COUNTER_2
  rom0e:447e
    notes:
      Reads a byte from the stream into the alt loop counter.
0e:4485..44a5 function: [ch0.4] MUSIC_CHANNEL_0_PITCH_EFFECT
  rom0e:4485
    notes:
      Reads two byte address from channel 0 stream into the pitch
      control addresses.
0e:44a6..44ae function: [ch0.5] MUSIC_CHANNEL_0_WAVE_DUTY
  rom0e:44a6
    notes:
      Reads a byte from the stream directly into the hw wave duty register.
      (Backup in control structure if music state needs to be restored.)
0e:44af..44c0 function: [ch0.6] MUSIC_CHANNEL_0_PAN
  rom0e:44af
    notes:
      Sets channel 0 pan based on a byte from the stream.
        0: (mute)
        1: right
        2: left
        3: stereo
0e:44c1..44c4 pan lookup table for music channel 1 (hw channel 1)
0e:44c5..44cb function: [ch0.7] MUSIC_SET_TEMPO
  rom0e:44c5
    notes:
      Reads a byte from the stream into tempo (cb01). This command is
      only valid when encoded for channel 0.
0e:44cc..45a4 top level processing for game music channel 1 (hw channel 1)
0e:45a5..45aa function: DISPATCH_MUSIC_CHANNEL_1_COMMAND
  rom0e:45a5
    inputs:    hl <-- indexed jump table address
0e:45ab..45c2 jump table for music channel 1 commands [ch1.x]
0e:45c3..45dd function: [ch1.0] MUSIC_CHANNEL_1_VOLUME_EFFECT
  rom0e:45c3
0e:45de..45ed function: [ch1.1] MUSIC_CHANNEL_1_JUMP
  rom0e:45de
0e:45ee..45f4 function: [ch1.3] MUSIC_CHANNEL_1_LOOP_COUNTER
  rom0e:45ee
0e:45f5..45fb function: [ch1.a] MUSIC_CHANNEL_1_LOOP_COUNTER_2
  rom0e:45f5
0e:45fc..461c function: [ch1.4] MUSIC_CHANNEL_1_PITCH_EFFECT
  rom0e:45fc
0e:461d..462c function: [ch1.5] MUSIC_CHANNEL_1_WAVE_DUTY
  rom0e:461d
0e:462d..4647 function: [ch1.6] MUSIC_CHANNEL_1_PAN
  rom0e:462d
0e:4648..464b pan lookup table for music channel 0 (hw channel 2)
0e:464c..46ff top level processing for game music channel 2 (hw channel 3, wave)
0e:4700..4705 function: DISPATCH_MUSIC_CHANNEL_2_COMMAND
  rom0e:4700
    inputs:    hl <-- indexed jump table address
0e:4706..471d jump table for music channel 2 commands [ch2.x]
0e:471e..4726 function: [ch2.0] MUSIC_CHANNEL_2_VOLUME
  rom0e:471e
    notes:
      This channel doesn't support complicated volume effect (as a reflection
      of the capabilities of the hw wave channel that it maps to). One byte
      is simply read into ff1c (backup at cb40).
0e:4727..4736 function: [ch2.1] MUSIC_CHANNEL_2_JUMP
  rom0e:4727
0e:4737..473d function: [ch2.3] MUSIC_CHANNEL_2_LOOP_COUNTER
  rom0e:4737
0e:473e..4744 function: [ch2.a] MUSIC_CHANNEL_2_LOOP_COUNTER_2
  rom0e:473e
0e:4745..4765 function: [ch2.4] MUSIC_CHANNEL_2_PITCH_EFFECT
  rom0e:4745
0e:4766..4777 function: [ch2.6] MUSIC_CHANNEL_2_PAN
  rom0e:4766
0e:4778..477b pan lookup table for music channel 2 (hw channel 3)
0e:477c..47aa function: [ch2.8] MUSIC_CHANNEL_2_WAVE_SAMPLE
  rom0e:477c
    notes:
      Reads two byte address from stream. New wave sample data is read from
      this address.
0e:47ab..47ab (end of UPDATE_MUSIC_STREAMS)
0e:47ac..47b4 function: RESTART_MUSIC_CHANNEL_EFFECT
  rom0e:47ac
    inputs:    hl <-- address of effect timer (e.g. cb06)
    notes:
      Timer is reset to 1 (so effect processing will start on the next update)
      and current address (e.g. cb09..cb0a) is set to starting address (e.g.
      cb07..cb08). Can be used for pitch effects or volume effects.
0e:47b5..47bc octave offset lookup table
0e:47bd..47c8 function: MUSIC_CHANNEL_0_STREAM_READ_1
  rom0e:47bd
    outputs:    a <-- byte read from stream
    notes:
      Stream pointer is advanced.
0e:47c0..47c8 function: MUSIC_STREAM_READ_1
  rom0e:47c0
    inputs:    hl <-- address of stream pointer
    outputs:    a <-- byte read from stream
0e:47c9..47cd function: MUSIC_CHANNEL_1_STREAM_READ_1
  rom0e:47c9
    outputs:    a <-- byte read from stream
0e:47ce..47d2 function: MUSIC_CHANNEL_2_STREAM_READ_1
  rom0e:47ce
    outputs:    a <-- byte read from stream
0e:47d3..484c function: UPDATE_MUSIC_CHANNEL_0_EFFECTS
  rom0e:47d3
    notes:
      Called every third frame (controlled by ffb4). Handles processing
      for volume and pitch effects for music channel 0.
0e:484d..48c6 function: UPDATE_MUSIC_CHANNEL_1_EFFECTS
  rom0e:484d
0e:48c7..4915 function: UPDATE_MUSIC_CHANNEL_2_EFFECTS
  rom0e:48c7
    notes:
      Note that this channel has pitch effect but no volume effect.
0e:4916..491f function: HANDLE_EFFECT_STREAM_JUMP
  rom0e:4916
    inputs:    hl <-- effect stream
    outputs:   hl <-- new effect stream
                a <-- new byte from stream
    notes:
      After reading 0 from effect stream (and advancing pointer), this
      will be called. Two byte address will be read from the stream and
      used as the new stream position. Note that this position is written
      to the end of the old stream. Hopefully this will always do nothing
      as this is usually a high ROM address, but it is probably a bug.
      (Perhaps this was intended to write the address back to the
      >music_control structure, but this is done later by the caller).
0e:4920..4948 function: PLAY_SOUND
  rom0e:4920
    inputs:     a <-- sound id (should not be zero)
    notes:
      Starts playing the given sound. Steals music channel 1 (hw channel 1)
      for the duration.
0e:4949..49f4 function: UPDATE_SOUND
  rom0e:4949
    notes:
      Should be called once per frame to update the currently playing sound.
      First half processes the tone stream and second half processes the
      noise stream.
0f:6038..607f unused (?)
  rom0f:60e8
    notes:
      Garbage code fragment matching f:63b5..63fc. Differences are references
      to absolute addresses also offset by 0x37d. Probably leftover from
      earlier version and unused.
0f:6080..6097 various entry points for functions in the 6080..655f region
0f:6098..6145 function: MONSTER_GFX_SETUP
  rom0f:6098, rom0f:6080, rom00:01d4, rom00:18a7
    notes:
      Clears memory related to monster gfx and battle animation (d920..daff).
      Also clears VRAM and cc00 oam staging area. Then loads various standard
      battle gfx, sets up monster gfx and plays the monster spawning animation.
      d98b is setup based on the value of c7f3.
0f:6146..615d function: LOAD_MONSTER_GFX_TILEMAPS
  rom0f:6146, rom0f:6086
    notes:
      Calls LOAD_MONSTER_GFX_TILEMAP for each enemy group that still has
      remaining members.
0f:615e..617f function: LOAD_MONSTER_GFX_TILEMAP
  rom0f:615e
    inputs:  ff90 <-- enemy group index
    notes:
      Loads the tilemap data (sequentially increasing numbers) into VRAM
      for the given monster (using already-calculated metadata).
0f:6166..617f function: DRAW_MONSTER_GFX_TILEMAP
  rom0f:6166, rom0f:6089
    inputs:     c <-- width (in tiles)
                b <-- height (in tiles)
                d <-- offset (tile index of first tile)
               hl <-- vram address
0f:6180..618f function: GET_MONSTER_GFX_DIMENSIONS_AND_OFFSET
  rom0f:6180, rom0f:608c
    inputs:  ff90 <-- enemy group index
    outputs:    c <-- width (from d927)
                b <-- height (from d927)
                d <-- offset (from d92d)
0f:6190..61b7 function: LOAD_MONSTER_GFX_BACKGROUND
  rom0f:6190
    notes:
      Usually clears the monster gfx tilemap region to ff (white tile),
      but for the Arsenal fight, loads the cloud background (the tiles
      of which are part of the monster gfx).
0f:61a4..61b7 function: LOAD_ARSENAL_CLOUD_BACKGROUND_LOWER
  rom0f:61a4, rom0f:608f
    notes:
      Loads the Arsenal cloud background into the lower region of the
      screen that is usually covered by the battle window.
0f:61a9..61b7 function: LOAD_ARSENAL_CLOUD_BACKGROUND
  rom0f:61a9
    inputs:    hl <-- starting vram address
                c <-- number of pairs of rows to load
0f:61b8..61c9 function: LOAD_ARSENAL_CLOUD_BACKGROUND_ROW_1
  rom0f:61b8
    inputs:    hl <-- starting vram address
    notes:
      Loads a row of Arsenal's cloud background. (Rows alternate types.)
0f:61bd..61c9 function: LOAD_ARSENAL_CLOUD_BACKGROUND_ROW_0
  rom0f:61bd
    inputs:    hl <-- starting vram address
    notes:
      Loads a row of Arsenal's cloud background. (Rows alternate types.)
0f:61ca..61e2 function: GET_MONSTER_GFX_TILEMAP_ADDRESS
  rom0f:61bd, rom0f:6095
    inputs:     a <-- enemy group index
    outputs:   hl <-- tilemap address
    notes:
      Calculates the starting address for the monster gfx tilemap of the given
      enemy group (based on offsets stored in d936 and d939).
0f:61e3..61eb function: ARSENAL_INITIALIZE
  rom0f:61e3
    notes:
      Called for Arsenal instead of MONSTER_GFX_SPAWN_ANIMATION.
      Enables the cloud background and cloud animation.
0f:61ec..61ec function: SPECIAL_ENCOUNTER_INITIALIZE
  rom0f:61ec
    notes:
      Does nothing. Called for special encounters (d98b) instead of
      MONSTER_GFX_SPAWN_ANIMATION.
0f:61ed..623f function: MONSTER_GFX_SPAWN_ANIMATION
  rom0f:61ed
    notes:
      Monster spawning animation (uses scanline animation of scroll Y).
0f:6240..6256 function: MONSTER_GFX_SPAWN_ANIMATION_HELPER
  rom0f:6240
    inputs:  ff91 <-- repeat count
    notes:
      Loads c800..c83f buffer used for scroll Y register control. Buffer
      will be loaded with sequential numbers starting with 0 each
      repeated according to the repeat count. Combined with the fixed
      offsets from f:629e, this gives scroll Y values for each line.
0f:6257..627e function: LOAD_MONSTER_GFX_OFFSET_AND_SIZE
  rom0f:6257, rom0f:6092
    notes:
      Calls LOAD_MONSTER_GFX_DIMENSIONS, then uses this to populate the
      monster gfx data in d92d.
0f:627f..629d function: LOAD_MONSTER_GFX_TILES
  rom0f:627f
    notes:
      Loads monster gfx tile data into vramfor all three of the
      current enemy groups.
0f:629e..62a1 scroll Y offsets for MONSTER_GFX_SPAWN_ANIMATION
0f:62a2..62a5 repeat counts for MONSTER_GFX_SPAWN_ANIMATION
0f:62a6..62ac special encounters (d98b 1)
0f:62ad..62e8 function: PROCESS_MONSTER_GFX
  rom0f:62ad, rom0f:6083, rom00:01e3, rom00:18b5
    notes:
      For each enemy, if stack size is 0, PROCESS_MONSTER_GFX_DEATH
      (playing death animation if necessary), if stack is lifted,
      PROCESS_MONSTER_GFX_LIFT (unlifting). Then if faded out,
      PBA_FADE_IN.
0f:62e9..640e function: PROCESS_MONSTER_GFX_DEATH
  rom0f:62e9
    inputs:     c <-- enemy group index
    notes:
      If enemy hasn't already died (d90d), set the died flag. Then
      play the death animation if necessary. Death animation is skipped
      forlifted enemies. If monsters are faded out, the part of the
      animation that erased the monster gfx tiles will play, but the
      sprite animation won't. Sprite animation uses the d943 buffer
      for oam y, x coordinates. Arsenal has special handling.
0f:640f..642d function: ARSENAL_DEATH_SHAKE
  rom0f:640f
    inputs:     b <-- length
      Shakes screen with scroll X register will playing sound. Used
      during Arsenal's death animation.
0f:642e..6438 function: ENEMY_DEATH_CLEAR_ROW
  rom0f:642e
    inputs:  ff91 <-- width
               hl <-- address
    notes:
      Clears a row of monster gfx tilemap data at the given address.
0f:6439..643f function: LOAD_ENEMY_DEATH_TOP_WRITE_POSITION
  rom0f:6439
    outputs:   hl <-- address
    notes:
      Loads address previously stored by STORE_ENEMY_DEATH_TOP_WRITE_POSITION.
0f:6440..6448 function: STORE_ENEMY_DEATH_TOP_WRITE_POSITION
  rom0f:6440
    inputs:    hl <-- address
    notes:
      Stores hl into d975..d976. This is the address of the next vram address
      that will be cleared from the top of the monster gfx during enemy death.
0f:6449..644f function: LOAD_ENEMY_DEATH_BOTTOM_WRITE_POSITION
  rom0f:6449
    outputs:   hl <-- address
    notes:
      Loads address previously stored by STORE_ENEMY_DEATH_BOTTOM_WRITE_POSITION.
0f:6450..6458 function: STORE_ENEMY_DEATH_BOTTOM_WRITE_POSITION
  rom0f:6450
    inputs:    hl <-- address
    notes:
      Stores hl into d977..d978. This is the address of the next vram address
      that will be cleared from the bottom of the monster gfx during enemy death.
0f:6459..6470 function: ENEMY_DEATH_STAGE_OAM
  rom0f:6459
    notes:
      Stages oam data for the enemy death animation at cc00 based on the data
      in the d943 buffer.
0f:6471..64da function: PROCESS_MONSTER_GFX_LIFT
  rom0f:6471
    notes:
      Plays the animation to return a lifted monster to its original position.
0f:64db..64e7 function: GET_MONSTER_GFX_DIMENSIONS_3
  rom0f:64db
    notes:
      Same as function: GET_MONSTER_GFX_DIMENSIONS, but different bank.
0f:64e8..64e8 ? (unused, looks like default palette, maybe part of following table)
0f:64e9..64ea palette lookup table for enemy death animation
0f:64eb..655f unused (?)

*********************
CSCRIPT FUNCTION LIST
*********************

0c:4696..472e function: CHECK_SURPRISE
  cscript:4696; cscript[00]
    outputs: d800 <-- 0: no surprise, 1: player surprise, 2: enemy surprise
    notes:
      This is the 00 cscript entry point.
0c:472f..4772 function: SURPRISE_HELPER
  cscript:472f
    inputs:    R0 <-- first battler
               R1 <-- last battler
    outputs: de00 <-- item_data[1] for all items for all battlers in range combined by bitwise-and
    notes:
      item_data[1] contains the flags for the warning and surprise items.
0c:4773..47de function: RUN_AWAY
  cscript:4773; cscript[01]
    outputs: d801 <-- 0: run failed; 1: run succeeded
    notes:
      If run fails, d800 is also set to 2 (equivalent to enemy surprise).
      This is the 01 cscript entry point.
0c:47df..4817 function: RUN_HELPER
  cscript:47df
    inputs:    R0 <-- first battler
               R1 <-- last battler
    outputs: R49w <-- sum of agility
    notes:
      Adds agility of battlers in the given range.
      Dead player characters are counted as 100. Dead monsters are not counted.
      (Stacks are counted but once.)
0c:4818..49e4 function: MONSTER_AI
  cscript:4818; cscript[02]
    notes:
      Results are written into battle_stat records.
      This is the 02 cscript entry point.
0c:49e5..4ace function: RESOLVE_TURN_ORDER
  cscript:49e5; cscript[03]
    notes:
      Result is written to d803..??. Two-byte record where second byte is battler index 
      and first byte is sub-battler index (stack index for enemy stacks).
      Data is terminated by 0xFF.
      This is the 03 cscript entry point.
0c:4acf..4b97 function: HANDLE_CONFUSION
  cscript:4acf; cscript[04]
    inputs:  d802 <-- index of current actor
    notes:
      Actor data is read from the d803.. array (from RESOLVE_TURN_ORDER).
      Replaces the item and target info in >battle_stat for the confused character.
      This is the 04 cscript entry point.
      As noted by Alex Jackson, there is a bug where the item slot info is set incorrectly;
      it is set to 3 times the slot number (indexing from 0) instead of the slot number.
0c:4b98..4c42 function: HANDLE_POISON
  cscript:4b98; cscript[06]
    notes:
      Processes poison damage for all battle participants.
      This is the 06 cscript entry point.
      As noted by Alex Jackson, there is a bug. If an actor dies from poison, the wrong
      message is printed; the game uses battle script 0x24 instead of battle script 7.
      Battle script 0x24 pulls the name from d90a which isn't initialized by this cscript.
      In particular, if the last executed combat action had no target, this memory address
      will hold 8 (out of bounds) which will result in #text-name trying to read a name
      from d802. Reading random data as text is actually dangerous because text is displayed
      by passing it to the script interpreter, so lots of bad things can happen. The glitched
      speedrun by Poxnor exploits this bug. d802 is the actor index which is equal to the total
      number of actors when the poison script is run. If this is 16, the game will execute
      npc-refresh which, among other things, clears the high bit in all of d000..dfff.
      (Normally, npc-refresh expects to be executed during map mode where d000..dfff holds
      the map and the high bit is the npc flag.)
      All-target attacks are maybe also exploitable, but this may be hard as d902 may be harder
      to populate.

********************
BATTLE ITEM CSCRIPTS
********************

Adjusted strength:
  Start with the strength stat.
  If Cursed, divide by 2.
  If Power Magi is equipped, add 5 plus the number of Power Magi.
Adjusted agility:
  Start with the agility stat.
  If Blind, divide by 2.
  If Speed Magi is equipped, add 5 plus the number of Speed Magi.
Double-adjusted agility:
  Start with the adjusted agility.
  If Blind, divide by 2 (again).
  If Speed Magi is equipped, add 5 plus the number of Speed Magi (again).
  (Probably this is a bug, and normal adjusted agility should be used instead.)
Adjusted defense:
  Start with the defense stat.
  If Cursed, divide by 2.
  If Defense Magi is equipped, add 5 plus the number of Defense Magi.
Adjusted mana:
  Start with the mana stat.
  If Mana Magi is equipped, add 5 plus the number of Mana Magi.

item script 00: Cure book
  heal group
  attack = Attacker's adjusted mana
  defense = Defender's adjusted mana
  multiplier = item param
  recovery = multiplier * (attack + defense) + random(0, attack)

item script 01: Cure Potion
  recovery = item param + random(0, item param 2)

item script 02: status potion
  remove status based on the bitwise complement of item param 1

item script 03: nothing

item script 06: Shield
  script actual does nothing except printing message

item script 07: Elemental shield
  applies elemental flags from item param to party (or enemy) resist (in >battle_data).
  (These will last the whole battle, but only applied when the action is taken.)

item script 08: Counterattack weapon (Revenge sword)
  script actual does nothing except printing message

item script 09: heal all magic

item script 0a: Single target strength weapon with elemental bonus (Flame sword)
  elementals = item param 2
  if enemy weaknesses overlap elementals, use item script 29.
  otherwise use item script 31.

item script 0b: heart

item script 0c: Single target strength weapon with monster family bonus (Coral Sword)
  family = high nybble of item param 2 (low nybble must be zero)
  if family matches target, use item script 29.
  otherwise use item script 31.

item script 0d: Draining single target mana weapon (Vampic)
  can be blocked
  hit bonus = Attacker's double-adjusted agility
  evade bonus = Defender's adjusted agility
  family = high nybble of item param 2 (low nybble must be zero)
  attack = Attacker's adjusted mana
  multiplier = item param
  damage = multiplier * attack + random(0, attack)
  (no damage mitigation)
  if family matches target:
    damage is dealt to the attacker instead
    defender heals damage (but not more HP than the attacker started with)
  otherwise:
    damage is dealt to the defender
    if target family is neither 0 nor 1:
      attacker heals damage (but not more HP than the defender started with)
  note:
    The family 0 or 1 condition has sometimes been misstated. Family 0 is plants
    (fungus, flower, tree) and family 1 is constructs (pebble, woodman, sword).
    However, lots of miscellaneous stuff is also family 0 (like Apollo), maybe
    just due to oversight. (This has nothing to do with O-Para.)
    (Debatably, certain things being family 0 could be a bug.)

item script 0e: Standard single target agility weapon (Rapier)
  can be blocked
  hit bonus = Attacker's double-adjusted agility
  evade bonus = Defender's adjusted agility
  base hit rate = 97
  attack = Attacker's double-adjusted agility
  defense = Defender's adjusted defense
  multiplier = item param
  damage = multiplier * attack + random(0, attack) - 5 * defense
  if target used a shield:
    damage /= 2
  if target has O-Weapon:
    damage /= 2
  susceptible to defending items

item script 0f: Standard single target mana weapon (Psi dagger)
  can be blocked
  hit bonus = Attacker's double-adjusted agility
  evade bonus = Defender's adjusted agility
  base hit rate = 97
  attack = Attacker's adjusted mana
  defense = Defender's adjusted defense
  multiplier = item param
  damage = multiplier * attack + random(0, attack) - 5 * defense
  if target used a shield:
    damage /= 2
  if target has O-Weapon:
    damage /= 2
  susceptible to defending items

item script 10: XCalibur
  group attack
  always hits, cannot be blocked
  attack = max(Attacker's adjusted strength, item param 2)
  defense = Defender's defense (non-adjusted)
  multiplier = item param
  damage = multiplier * attack + random(0, attack) - 5 * defense
  if target has O-Weapon:
    damage /= 2

item script 11: Single target absolute strength weapon (Glass sword, Masmune)
  always hits, cannot be blocked
  attack = Attacker's adjusted strength
  bonus = 2-byte item param
  damage = bonus + random(0, attack)
  (no damage mitigation)

item script 12: saw
  can be blocked
  hit bonus = Attacker's adjusted strength
  hit rate = item param + bonus
  (no evade bonus)
  success rate = Attacker's adjusted strength + item param 2
  if target used a shield:
    success rate /= 2
  if target has O-Weapon:
    success rate /= 2
  if success rate > Defender's adjusted defense
    instant kill
  target immune if monster id >= 0xf8

item script 13: gun (Colt)
  hit bonus = Attacker's adjusted strength
  hit rate = item param2 + 2 * bonus
  (no evade bonus)
  attack bonus = 2 for robots or 1 otherwise
  attack = random(0, item param2) * attack bonus
  defense = Defender's adjusted defense
  damage = (5 * item param1) + attack - 5 * defense
  if target used a shield:
    damage /= 2
  if target has O-Weapon:
    damage /= 2
  note:
    This attack bonus can leak to the bow script.

item script 14: bow (Bow)
  hit bonus = Attacker's adjusted agility (not double-adjusted)
  hit rate = item param2 + 2 * bonus
  (no evade bonus)
  attack bonus = 0 (see note)
  attack = random(0, item param2) * attack bonus
  defense = Defender's adjusted defense
  damage = (5 * item param1) + attack - 5 * defense
  if target used a shield:
    damage /= 2
  if target has O-Weapon:
    damage /= 2
  note:
    "Attack bonus" (cf89) is unitialized by this script. If certain other scripts
    are used in the same battle, this value (1 or 2) can leak into bow damage.
    Perhaps this is meant to be 1 anyway.

item script 15: multi bow (SMG, Samurai Bow)
  group attack
  always hits, cannot be blocked
  attack bonus = 2 for robots or 1 otherwise
  attack = (random(0, item param) / 2) * attack bonus
  defense = Defender's defense adjusted by Magi (but not Curse)
  damage = (5 * item param1) + attack - 5 * defense
  if target has O-Weapon:
    damage /= 2
  note:
    This attack bonus can leak to the bow script.
    Also interesting that Samurai bow item data populates param2 but it isn't used
    here. Maybe the random roll was meant to be based on param2 (which would make
    this item weaker).

item script 16: laser
  always hits, cannot be blocked
  attack = random(0, item param2)
  damage = (5 * item param1) + attack
  (no damage mitigation)

item script 17: all gun (Missile, NukeBomb)
  all attack
  always hits, cannot be blocked

item script 18: whip
  can be blocked
  hit bonus = Attacker's double-adjusted agility
  evade bonus = Attacker's adjusted agility
  base hit rate = 97
  attack = Attacker's adjusted strength
  defense = Defender's adjusted defense
  multiplier = item param
  damage = multiplier * attack + random(0, attack) - 5 * defense
  if target used a shield:
    damage /= 2
  if target has O-Weapon:
    damage /= 2
  param2 chance to "wind the whip" which removes the target's readied action
    if the target hasn't moved yet, they will take no action on their turn
    defensive actions will be canceled for the round
      (but persistent effect like resist will still exist)

item script 19: hyper
  all attack
  instantly kills all enemies
  enemies with monster_id > item param are immune
    (for Hyper, this is WarMach and beyond)

item script 1a: elemental magic (elemental magic book)
  group attack
  elementals = item param 2 (if 0, non-elemental)
  always hits, cannot be blocked
  can be reflected
  if target resists overlaps elementals, no effect
  attack = Attacker's adjusted mana
  defense = Defender's adjusted mana
  multiplier = item param
  if target weaknesses overlap elements:
    attack += 5
    defense = 0
  if user has an elemental magi equipped that overlaps elementals
    attack += 5 plus the number of the corresponding magi
    (there is a bug in detecting magi elementals; see note)
  base damage = attack * multiplier + random(0, attack)
  mitigation = (base damage * defense) / 200
    (overflow is a concern in this calculation)
  damage = base damage - mitigation
  note:
    As noted by Alex Jackson, the elemental flags for Thunder and Ice are
    reversed and the formula that compares these flags to the item's element
    actual derefences the item's element as a pointer.

item script 1b: status magic
  group attack
  elementals = item param 2
  can be reflected
  if target resists overlaps elementals, no effect
  status evade rate = 50 + 2 * (Defender's adjusted mana -- Attacker's adjusted mana)
  if target weakness overlaps elementals, status evade rate = 0
  status bits in param1 applied to targets
  if target is sleeping, set action to sleeping
  if target is paralyzed, set action to paralyzed
  if target is stunned, set hp to 0
  if stone/stun bits in param1, also reset stack size to 0

item script 1c: monster slayer magic (prayer book)
  group attack
  always hits, cannot be blocked
  family = high nybble of item param 2 (low nybble must be zero)
  attack = Attacker's adjusted mana
  multiplier = item param 1
  damage = attack * multiplier + random(0, attack)
  (no damage mitigation)
  if family matches target:
    do damage
  otherwise:
    no effect

item script 1d: martial arts
  can be blocked
  hit bonus = Attacker's double-adjusted agility
  evade bonus = Attacker's adjusted agility
  base hit rate = 97
  uses = number of remaining uses of item
  if attacker is a robot or uses is $fe (true for enemies):
    uses = item param2 - 5
  instead of hit roll being 0 .. 100, it is 0 ... uses
    (this means accuracy increases as the number of uses decreases)
  multiplier = item param 1
  attack = (item param 2 - uses)
  defense = Defender's adjusted defense
  damage = attack * multiplier + random(0, Attacker's double-adjusted agility) - 5 * defense
  if uses == 1:
    damage *= 3
  if target used a shield:
    damage /= 2
  if target has O-Weapon:
    damage /= 2
  susceptible to defending items
  note:
    item param 2 is sort of a reference point for the number of uses so that more advanced
    martial arts with fewer uses actually get a stronger attack score (even without leveling up).
    The difference between param 2 and the default uses goes up as martial arts gets stronger.
    However, this is in conflict with the algorithm for robots/enemies, as they can then end up
    using a number of uses that is less than the default number of uses for the item.

item script 1e: multi-hit
  as a normal strength weapon (item script 31) except:
  item param2 determines the number of hits
  each hit is handled independently with its own hit roll, damage roll, mitigation, etc.
  the attack is a miss only if all attempts miss
  the exception to independent behavior is that there is a single shield block roll to block all hits

item script 1f: status attack
  can be blocked
  hit bonus = Attacker's double-adjusted agility
  evade bonus = Attacker's adjusted agility
  base hit rate = 97
  if target resists overlaps elementals, no effect
  status evade rate = 50 + 2 * (Defender's adjusted mana -- Attacker's adjusted mana)
  if target weakness overlaps elementals, status evade rate = 0
  status bits in param1 applied to targets
  if target is sleeping, set action to sleeping
  if target is paralyzed, set action to paralyzed
  if target is stunned, set hp to 0
  if stone/stun bits in param1, also reduce stack size by 1
  susceptible to defending items
  note:
    Selects the first target in the stack that isn't already affected by the attack's status. However,
    as noted by Alex Jackson, this is a bug; it is also supposed to avoid enemies that already have
    Stone or Stun. (r90 is supposed to be $90; but at least r90 should be $00 here)

item script 20: stat debuff
  group attack
  target stat determined by item param (0 str, 1 agl, etc)
  evade rate = (raw) target stat -- item param 2
  reduces stat by 1 + ((raw) target stat / 4)
  cannot reduce below zero
  lasts for the rest of the battle

item script 21: elemental all magic
  all attack
  cannot be reflected
  otherwise, as elemental magic (item script 1a)

item script 22: explode
  as item script 19 (hyper), but also kill the attacker

item script 23: steal
  hit rate = Attacker's adjusted agility + item param
  amount = Attacker's adjusted agility * item param 2
  if the party is stealing:
    adds amount to party's gp total
  if enemy is stealing:
    removes amount from party's gp total

item script 24: dna
  group attack
  can be reflected
  only affects targets with race <= item param 2
    (for DNA, this means that robots are immune)
  deals random(0, item param) damage
  note:
    As noted by Alex Jackson, there is a bug in this script. It is probably supposed to do
    target's max hp * item param 1 / 100 + random(0, item param); however, 100 ($64) was
    mistakenly replaced with r64 which is expected to be 0, killing this term.

item script 25: buff (nothing uses this directly)
  group attack
  target stat determined by item param (0 str, 1 agl, etc)
  stat is increased by item param 2, capped at 99 (even is stat exceeded 99 before)
  lasts for the rest of the battle

item script 26: self buff
  as item script 25, but targets the user's own group

item script 27: (Dissolve)
  can be blocked
  hit bonus = Attacker's double-adjusted agility
  evade bonus = Attacker's adjusted agility
  base hit rate = 97
  attack = Attacker's adjusted mana
  defense = Defender's adjusted mana
  multiplier = item param
  damage = multiplier * attack + random(0, attack) - defense
  attacker heals damage (but not more HP than the defender started with)
  susceptible to defending items

item script 28: status all magic
  all attack
  cannot be reflected
  otherwise, the same as script 1b (status magic)

item script 29: Critical single target strength weapon (Critical)
  always hits, cannot be blocked
  attack = Attacker's adjusted strength
  multiplier = item param
  damage = multiplier * attack + random(0, attack)
  (no damage mitigation)
  31/101 chance of "Mighty Blow" that instantly kills. (Monster immune if id > 0xf0.)
  note:
    monster id > 0xf0 is almost certainly a buggy condition (it splits between Human M
    and Human F). Not clear is the intention is supposed to be that just bosses are immune,
    or players are immune too. (Not all bosses are in this region, but many important ones are.)
    I don't think players really need this immunity, and player monsters don't get it
    (after initial transformation), so I'd be inclined to change this to reflect that, but a more conservative change would just make Human M
    match the others.

item script 2a: multiply
  increases the user's current and max stack size by one
    (doesn't work if the max stack size is already 9)
  the new combatant starts with 80% HP.

item script 2b: Apollo's Aegis
  prints story text and changes music based on the curren round number

item script 2c: Apollo's first masmune
  prints story text and triggers graphical transformation
  Otherwise, the same as item script 11 (usual Masmune script)

item script 2d: Apollo's subsequent masmune
  checks for deterioration (< 10000 HP or round counter > 20)
  Otherwise, the same as item script 11 (usual Masmune script)

item script 2e: Apollo's flare
  checks for deterioration (< 10000 HP or round counter > 20)
  Otherwise, the same as item script 21 (usual Flare script)

item script 2f: smasher

item script 30: Apollo's explosion
  base damage = (item param 1) * 256 + random(0, item param 2)
  if the guest party member (Dad) is alive, they will take the damage
    instead of each other party member. The damage is divided by the number of other living
    party members. (But in this case, the damage is virtual, and Dad doesn't die until the
    end and is forced to die at the end.)
  otherwise, each living party member is hit for base damage / 4 (each gets a separate roll)

item script 31: Standard single target strength weapon (Hammer)
  can be blocked
  hit bonus = Attacker's double-adjusted agility
  evade bonus = Attacker's adjusted agility
  base hit rate = 97
  attack = Attacker's adjusted strength
  defense = Defender's adjusted defense
  multiplier = item param
  damage = multiplier * attack + random(0, attack) - 5 * defense
  if target used a shield:
    damage /= 2
  if target has O-Weapon:
    damage /= 2
  susceptible to defending items

*****
OTHER
*****

== FX ==

There are two special screen FX that the game supports:
  01 -- the horrible shaking we see in the last dungeon
  02 -- the wave effect we see underwater in Apollo's world
The FX is controlled by the upper nybble of c305.
This is also a script variable, so FX can be controlled from scripts.
Many asm functions will temporarily disable FX by setting this variable to 0 and then restore it later.
One of the things that does this is the map script handler itself.
This means there needs to be special handling to allow map scripts to persistently change FX.
  00 -- restore FX on return (this is the default)
  03 -- clear FX on return

== PARTICLES ==

Particle system (EXECUTE_PARTICLES) controls up to 0x28 particles each mapped to
ones or more sprites (OAM objects). The game loops until all particles are dead.
Each loop, each particle is processed in order. If its start delay is non-zero,
it will be decremented and other processing is skipped. Processing is also skipped
for dead particles. Otherwise, x and y are modified by the velocity value. (Velocity
is relative to 0x80, so 0x80 is 0, 0x81 is +1, 0x7f is -1, etc.) Then, the OAM data
is staged based on the oam type. Lastly, the update function is run. After all
particles are processed, the global update function is run. The system does not
render directly; typically the global update will render one or more frames.
Lastly, the exit condition is checked.

Fields marked as "user data" are not used by the main system; they are only
used by the update or global update function (if they are used at all).

Memory map:
  c800..c827 start delay
  c828..c84f alive (00 alive, ff dead)
  c850..c89f x, y position
  c8a0..c8c7 user data (1)
  c8c8..c8ef oam type index
  c8f0..c917 user data (2)
  c918..c93f user data (3)
  c940..c98f x, y velocity (base 0x80 is zero)
  c990..c991 pointer to next block in OAM staging region (usually ccxx)
  c992       particle update function index
  c993       global update function index
  c994       particle count
  c995       user data
